IT TIP

응용 프로그램 스레드를 안전하게 만드는 방법은 무엇입니까?

itqueen 2020. 12. 25. 10:43
반응형

응용 프로그램 스레드를 안전하게 만드는 방법은 무엇입니까?


특히 스레드 안전은 동일한 공유 데이터에 액세스하기 위해 여러 스레드가 필요하다는 것을 의미합니다. 하지만이 정의로는 충분하지 않은 것 같습니다.

누구든지 어플리케이션 스레드를 안전하게 만들기 위해해야 ​​할 일이나 처리 할 일을 나열 해 주 시겠습니까 ? 가능하면 C / C ++ 언어에 대한 답변을 제공하십시오.


함수가 스레드로부터 안전 할 수있는 방법에는 여러 가지가 있습니다.

재진입 할 수 있습니다 . 즉, 함수에는 상태가없고 전역 또는 정적 변수를 건드리지 않으므로 여러 스레드에서 동시에 호출 할 수 있습니다. 이 용어는 다른 스레드가 이미 함수 안에있는 동안 한 스레드가 함수에 들어갈 수 있도록 허용하는 것입니다.

중요한 섹션 이있을 수 있습니다 . 이 용어는 많이 사용되지만 솔직히 중요한 데이터를 선호 합니다 . 중요 섹션은 코드가 여러 스레드에서 공유되는 데이터에 닿을 때마다 발생합니다. 그래서 저는 그 중요한 데이터에 초점을 맞추는 것을 선호합니다.

뮤텍스를 적절하게 사용 하면 중요한 데이터에 대한 액세스를 동기화하여 스레드 안전하지 않은 수정으로부터 적절하게 보호 할 수 있습니다. 뮤텍스와 잠금은 매우 유용하지만 큰 힘에는 큰 책임이 따릅니다. 동일한 스레드 내에서 동일한 뮤텍스를 두 번 잠그면 안됩니다 (즉, 자체 교착 상태). 둘 이상의 뮤텍스를 획득하면 교착 상태에 대한 위험이 증가하므로주의해야합니다. 뮤텍스로 데이터를 지속적으로 보호해야합니다.

모든 함수가 스레드로부터 안전하고 모든 공유 데이터가 적절하게 보호된다면 애플리케이션은 스레드로부터 안전해야합니다.

Crazy Eddie가 말했듯이 이것은 거대한 주제입니다. 부스트 스레드를 읽고 그에 따라 사용하는 것이 좋습니다.

낮은 수준의주의 사항 : 컴파일러는 스레드 안전성을 손상시킬 수있는 문을 재정렬 할 수 있습니다. 코어가 여러 개인 경우 각 코어에는 자체 캐시가 있으며 스레드 안전성을 유지하려면 캐시를 적절하게 동기화해야합니다. 또한 컴파일러가 명령문을 재정렬하지 않더라도 하드웨어가 될 수 있습니다. 따라서 완전하고 보장 된 스레드 안전성은 오늘날 실제로 불가능합니다. 그래도 99.99 %의 방법을 얻을 수 있으며 컴파일러 공급 업체 및 CPU 제조업체와 함께이 느린 경고를 수정하기위한 작업을 수행하고 있습니다.

어쨌든, 클래스를 스레드로부터 안전하게 만들기위한 체크리스트를 찾고 있다면 :

  • 스레드간에 공유되는 모든 데이터를 식별합니다 (놓치면 보호 할 수 없습니다).
  • 구성원을 만들고 boost::mutex m_mutex해당 공유 구성원 데이터에 액세스하려고 할 때마다 사용합니다 (이상적으로는 공유 데이터는 클래스 전용이므로 적절하게 보호하고 있음을 확신 할 수 있습니다).
  • 전역을 정리하십시오. 어쨌든 전역은 나쁘고 전역으로 스레드로부터 안전한 작업을 수행하려는 행운을 빕니다.
  • static키워드를 조심하십시오 . 실제로 스레드로부터 안전하지 않습니다. 따라서 싱글 톤을 시도하는 경우 제대로 작동하지 않습니다.
  • 이중 확인 잠금 패러다임을주의하십시오. 그것을 사용하는 대부분의 사람들은 미묘한 방식으로 잘못 이해하고 낮은 수준의 경고로 인해 파손되기 쉽습니다.

불완전한 체크리스트입니다. 생각하면 더 추가하겠습니다. 시작하기에 충분합니다.


두가지:

1. 전역을 사용하지 마십시오. 현재 전역이있는 경우 스레드 별 상태 구조체의 구성원으로 만든 다음 스레드가 구조체를 공통 함수에 전달하도록합니다.

예를 들어 다음으로 시작하면 :

// Globals
int x;
int y;

// Function that needs to be accessed by multiple threads
// currently relies on globals, and hence cannot work with
// multiple threads
int myFunc()
{
    return x+y;
}

상태 구조체를 추가하면 코드는 다음과 같습니다.

typedef struct myState
{
   int x;
   int y;
} myState;

// Function that needs to be accessed by multiple threads
// now takes state struct
int myFunc(struct myState *state)
{
   return (state->x + state->y);
}

이제 x와 y를 매개 변수로 전달하지 않는 이유를 물어볼 수 있습니다. 그 이유는이 예제가 단순화 되었기 때문입니다. 실생활에서 상태 구조체는 20 개의 필드를 가질 수 있으며 이러한 매개 변수 4-5 함수의 대부분을 전달하는 것은 어렵습니다. 여러 매개 변수 대신 하나의 매개 변수를 전달하는 것이 좋습니다.

2. 스레드에 공유해야하는 공통 데이터가있는 경우 중요한 섹션과 세마포어를 조사해야합니다. 스레드 중 하나가 데이터에 액세스 할 때마다 다른 스레드를 차단 한 다음 공유 데이터에 대한 액세스가 완료되면 차단을 해제해야합니다.


클래스의 메소드에 독점적으로 액세스하려면 이러한 함수에 잠금을 사용해야합니다.

다른 유형의 잠금 :

atomic_flg_lck 사용 :

class SLock
{
public:
  void lock()
  {
    while (lck.test_and_set(std::memory_order_acquire));
  }

  void unlock()
  {
    lck.clear(std::memory_order_release);
  }

  SLock(){
    //lck = ATOMIC_FLAG_INIT;
    lck.clear();
  }
private:
  std::atomic_flag lck;// = ATOMIC_FLAG_INIT;
};

원자 사용 :

class SLock
{
public:
  void lock()
  {
    while (lck.exchange(true));
  }

  void unlock()
  {
    lck = true;
  }

  SLock(){
    //lck = ATOMIC_FLAG_INIT;
    lck = false;
  }
private:
  std::atomic<bool> lck;
};

뮤텍스 사용 :

class SLock
{
public:
  void lock()
  {
    lck.lock();
  }

  void unlock()
  {
    lck.unlock();
  }

private:
  std::mutex lck;
};

그냥 윈도우 :

class SLock
{
public:
  void lock()
  {
    EnterCriticalSection(&g_crit_sec);
  }

  void unlock()
  {
    LeaveCriticalSection(&g_crit_sec);
  }

  SLock(){
    InitializeCriticalSectionAndSpinCount(&g_crit_sec, 0x80000400);
  }

private:
  CRITICAL_SECTION g_crit_sec;
};

원자및 atomic_flag는 스핀 카운트 스레드를 유지한다. Mutex 는 스레드를 잠자기 만합니다 . 대기 시간이 너무 길면 스레드를 잘 수 있습니다. 마지막 " CRITICAL_SECTION "은 시간이 소비 될 때까지 스레드를 스핀 수로 유지 한 다음 스레드가 휴면 상태가됩니다.

이 중요한 섹션을 사용하는 방법은 무엇입니까?

unique_ptr<SLock> raiilock(new SLock());

class Smartlock{
public:
  Smartlock(){ raiilock->lock(); }
  ~Smartlock(){ raiilock->unlock(); }
};

raii 관용구 사용. 중요 섹션을 잠그기위한 생성자와 잠금을 해제하기위한 소멸자.

class MyClass {

   void syncronithedFunction(){
      Smartlock lock;
      //.....
   }

}

This implementation is thread safe and exception safe because the variable lock is saved in the stack so when the function scope is ended (end of function or an exception) the destructor will be called.

I hope that you find this helpful.

Thanks!!


One idea is to think of your program as a bunch of threads commutating through queues. Each thread would have one queue, and these queues would be shared (along with a shared data synchronization method(such as a mutex, etc) ) to all of the threads.

Then "solve" the producer/consumer problem however you want to keep the queues from underflowing or overflowing. http://en.wikipedia.org/wiki/Producer-consumer_problem

As long as you keep your threads localized, just sharing data with by sending copies over the queue, and not accessing thread unsafe things like (most) gui libraries and static variables in multiple threads, then you should be fine.

ReferenceURL : https://stackoverflow.com/questions/5125241/how-to-make-an-application-thread-safe

반응형