1

Visual Studio 2010で試すためのマルチスレッドとダウンロードコードに関するこのチュートリアルをWebで見つけました。http://www.codeproject.com/Articles/14746/Multithreading-Tutorial

参考までに以下にコピーした「スレッドローカルストレージ」に関連するプログラムの1つ。2つのスレッドのように非常に単純に見え、どちらもクラスデータメンバー「m1」、「m2」、「m3」を増やします。この例では、「m2」は両方のスレッドからアクセスできる静的クラス変数です。

コードでは、ファイルの先頭にある「#defineWITH_SYNCHRONIZATION」でクリティカルセクションが有効になっていることに注意してください。私の理解では、「TMain()」の「forループ」はクリティカルセクションによって保護されているため、最初に「TMain()」に到達したスレッドは、他のスレッドからのインターリーブなしで、全体として50,000インクリメントを終了します。 '50,020 for m2'を出力すると、他のスレッドは残りを続行し、後で'100,020form2'を出力します。

しかし、いいえ、m2のプリントアウトはクリティカルセクションがまったくないように見えます。'm2'値は、次のような値でスクランブルされます。

スレッドt2:... m2 = 50376 ...
スレッドt1:... m2 =63964..。

私は基本的な何かを逃したに違いありません。それは何ですか?

以下のコードは、値を少し変更したWebページからのものであり、VStudioで簡単にコンパイルできます。

#include <stdio.h>
#include <windows.h>
#include <process.h>
#include <string>

using namespace std;

#define WITH_SYNCHRONIZATION

class ThreadX
{
private:
  int        m1;
  static int m2;                       // shared variable
  static  __declspec(thread)  int m3;  // thread local variable


#ifdef WITH_SYNCHRONIZATION
  CRITICAL_SECTION m_CriticalSection; 
#endif

public:
  string threadName;

  ThreadX()
  {
      m1 = 10;
#ifdef WITH_SYNCHRONIZATION
        InitializeCriticalSection(&m_CriticalSection);
#endif
  }

  virtual ~ThreadX()
  {
#ifdef WITH_SYNCHRONIZATION
       // Release resources used by the critical section object.
       DeleteCriticalSection(&m_CriticalSection);
#endif
  }

  void TMain(void) 
  {
#ifdef WITH_SYNCHRONIZATION
    EnterCriticalSection( &m_CriticalSection );
#endif

    for ( int i = 1; i <= 50000; i++ )
    {
        ++m1;  // init value 10
        ++m2;  // init value 20
        ++m3;  // init value 30
    }

    printf( "Thread %s: m1 = %d, m2 = %d, m3 = %d\n", threadName.c_str(), m1, m2, m3 );

#ifdef WITH_SYNCHRONIZATION
    LeaveCriticalSection( &m_CriticalSection );
#endif

  } 

  static unsigned __stdcall ThreadStaticTMain(void * pThis)
  {
      ThreadX * pthX = (ThreadX*)pThis;
      pthX->TMain();

      return 1;
  }

};

int ThreadX::m2 = 20;
int ThreadX::m3 = 30;

int main()
{
    // In this program we create 2 threads and request that their
    // entry-point-function be the TMain() function of the ThreadX
    // class.  Because _beginthreadex() cannot accept a class member
    // function we must employ a 2 step process involving a tricky
    // cast to accomplish this.

    ThreadX * o1 = new ThreadX();

    HANDLE   hth1;
    unsigned  uiThread1ID;

    hth1 = (HANDLE)_beginthreadex( NULL,         
                                   0,            
                                   ThreadX::ThreadStaticTMain,
                                   o1,           
                                   CREATE_SUSPENDED, 
                                   &uiThread1ID );

    if ( hth1 == 0 )
        printf("Failed to create thread 1\n");

    DWORD   dwExitCode;

    GetExitCodeThread( hth1, &dwExitCode );
    printf( "initial thread 1 exit code = %u\n", dwExitCode );

    o1->threadName = "t1";

    ThreadX * o2 = new ThreadX();

    HANDLE   hth2;
    unsigned  uiThread2ID;

    hth2 = (HANDLE)_beginthreadex( NULL,         
                                   0,            
                                   ThreadX::ThreadStaticTMain,
                                   o2,           
                                   CREATE_SUSPENDED, 
                                   &uiThread2ID );

    if ( hth2 == 0 )
        printf("Failed to create thread 2\n");

    GetExitCodeThread( hth2, &dwExitCode ); 
    printf( "initial thread 2 exit code = %u\n", dwExitCode );

    o2->threadName = "t2";

    ResumeThread( hth1 );   
    ResumeThread( hth2 );   

    WaitForSingleObject( hth1, INFINITE );  
    WaitForSingleObject( hth2, INFINITE );  

    GetExitCodeThread( hth1, &dwExitCode );
    printf( "thread 1 exited with code %u\n", dwExitCode );

    GetExitCodeThread( hth2, &dwExitCode );
    printf( "thread 2 exited with code %u\n", dwExitCode );

    // The handle returned by _beginthreadex() has to be closed
    // by the caller of _beginthreadex().

    CloseHandle( hth1 );
    CloseHandle( hth2 );

    delete o1;
    o1 = NULL;

    delete o2;
    o2 = NULL;

    printf("Primary thread terminating.\n");
    return 0;
}
4

3 に答える 3

2

CRITICAL_SECTION m_CriticalSection;ThreadXクラスのメンバー (インスタンス) 変数です。つまり、 のインスタンスを作成するたびにThreadX(これを 2 回行います)、新しいCRITICAL_SECTION. これは、各インスタンスが独自のクリティカル セクションに入り、問題なく、保護しようとしている変数を破棄するため、役に立ちません。

のドキュメントを見ると、各スレッドが入ろうとする をEnterCriticalSection1 つだけ作成することが言及されていることがわかります。CRITICAL_SECTION

CRITICAL_SECTION代わりに、のすべてのインスタンスが使用するものを 1 つだけ作成する必要がありThreadXます。理想的には、これは の静的メンバー変数になりThreadXます。ただし、C++ には静的コンストラクター(C# など) がないため、InitializeCriticalSection別の方法で呼び出しを行う必要があります。で初期化する静的変数にすることもできますmain()

于 2012-09-11T05:22:39.037 に答える
1

m_CriticalSection メンバーは、ThreadX クラスのインスタンスごとに異なります。したがって、各 ThreadX オブジェクトは独自のクリティカル セクションを使用します。これが、相互に影響を与えない理由です。

于 2012-09-11T05:18:23.027 に答える
0

これは、スレッドごとに 1 つのクリティカル セクションを定義するためです。main でグローバル クリティカル セクションを定義し、そのクリティカル セクションを各スレッド クラスに渡す必要があります。

于 2012-09-11T05:19:10.847 に答える