C++ 多线程中的关键节用法?

C++ 多线程中的关键节用法?,c++,multithreading,winapi,C++,Multithreading,Winapi,我在网上找到了这个关于多线程的教程,并下载了一些代码在VisualStudio2010上试用 其中一个程序与“线程本地存储”相关,我在下面复制了该程序以供参考。它看起来非常简单,只有两个线程,都增加了类数据成员“m1”、“m2”、“m3”。在本例中,“m2”是一个静态类变量,可由两个线程访问 请注意,在代码中,关键部分是在文件开头用“#define with_SYNCHRONIZATION”启用的。我的理解是,由于'TMain()'中的'for loop'受临界段保护,因此,无论哪个线程首先到达

我在网上找到了这个关于多线程的教程,并下载了一些代码在VisualStudio2010上试用

其中一个程序与“线程本地存储”相关,我在下面复制了该程序以供参考。它看起来非常简单,只有两个线程,都增加了类数据成员“m1”、“m2”、“m3”。在本例中,“m2”是一个静态类变量,可由两个线程访问

请注意,在代码中,关键部分是在文件开头用“#define with_SYNCHRONIZATION”启用的。我的理解是,由于'TMain()'中的'for loop'受临界段保护,因此,无论哪个线程首先到达'TMain()'都将作为一个整体完成50000个增量,而不与另一个线程交叉,打印出'50020 for m2',而另一个线程将继续其余线程,然后打印出'100020 for m2'

但是没有,m2的打印看起来根本没有关键部分。“m2”值与以下值相混淆:

线程t2:。。。m2=50376。。。 线程t1:。。。m2=63964

我一定错过了一些基本的东西。这是什么

有人帮忙吗

下面的代码是来自数值变化较小的网页,可以随时在VStuio中编译

#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;
}
#包括
#包括
#包括
#包括
使用名称空间std;
#定义与_同步
ThreadX类
{
私人:
int m1;
静态int m2;//共享变量
静态_declspec(thread)int m3;//线程局部变量
#ifdef与_同步
临界截面m临界截面;
#恩迪夫
公众:
字符串threadName;
ThreadX()
{
m1=10;
#ifdef与_同步
初始化CriticalSection(&m_CriticalSection);
#恩迪夫
}
虚拟~ThreadX()
{
#ifdef与_同步
//释放临界截面对象使用的资源。
DeleteCriticalSection(&m_CriticalSection);
#恩迪夫
}
无效(无效)
{
#ifdef与_同步
EnterCriticalSection(&m_CriticalSection);
#恩迪夫
对于(int i=1;i TMain();
返回1;
}
};
int-ThreadX::m2=20;
int ThreadX::m3=30;
int main()
{
//在这个程序中,我们创建2个线程并请求
//入口点函数是ThreadX的TMain()函数
//因为_beginthreadex()不能接受类成员
//功能我们必须采用两步流程,包括一个复杂的
//为了完成这一点,我们必须投下一票。
ThreadX*o1=新ThreadX();
处理hth1;
未签名的uiThread1ID;
hth1=(句柄)_beginthreadex(NULL,
0,            
ThreadX::ThreadStaticTMain,
o1,
创建挂起的,
&uiThread1ID);
如果(hth1==0)
printf(“未能创建线程1\n”);
DWORD dwExitCode;
GetExitCodeThread(hth1和dwExitCode);
printf(“初始线程1退出代码=%u\n”,dwExitCode);
o1->threadName=“t1”;
ThreadX*o2=新的ThreadX();
处理hth2;
未签名的uiThread2ID;
hth2=(句柄)_beginthreadex(NULL,
0,            
ThreadX::ThreadStaticTMain,
氧气,
创建挂起的,
&uiThread2ID);
如果(hth2==0)
printf(“未能创建线程2\n”);
GetExitCodeThread(hth2和dwExitCode);
printf(“初始线程2退出代码=%u\n”,dwExitCode);
o2->threadName=“t2”;
恢复线程(hth1);
恢复线程(hth2);
WaitForSingleObject(hth1,无限);
WaitForSingleObject(hth2,无限);
GetExitCodeThread(hth1和dwExitCode);
printf(“线程1已退出,代码为%u\n”,dwExitCode);
GetExitCodeThread(hth2和dwExitCode);
printf(“线程2已退出,代码为%u\n”,dwExitCode);
//必须关闭由_beginthreadex()返回的句柄
//由_beginthreadex()的调用方执行。
闭合手柄(hth1);
闭合手柄(hth2);
删除o1;
o1=零;
删除o2;
o2=零;
printf(“主线程终止。\n”);
返回0;
}

m_CriticalSection成员对于ThreadX类的每个实例都是不同的。因此,每个ThreadX对象都使用自己的critical section,这就是为什么它彼此不影响的原因。

这是因为您为每个线程定义了一个critical section。您需要在main中定义一个全局critical section,并将该critical section传递给每个线程类。

CRITICAL\u SECTION m\u CriticalSection;
ThreadX
类中的成员(实例)变量。这意味着每次创建
ThreadX
的实例时(执行两次),您正在创建一个新的
CRITICAL\u节
。这没有什么好处,因为每个实例都将进入自己的CRITICAL节,没有问题,并继续丢弃您试图保护的变量

如果您查看的文档,您会发现其中提到只创建一个
关键部分
,每个线程都会尝试进入该部分


相反,您只需要创建一个
CRITICAL\u部分,所有
ThreadX
实例都将使用该部分。理想情况下,这将是
ThreadX
中的一个部分。但是,(比如C#),因此您必须以其他方式调用
InitializeCriticalSection
。您可以始终将其设置为静态变量,并在
main()中初始化

@Tutankhamen,我认为最好使用范围适当的关键部分。如果它只用于
ThreadX
,那么它应该是
ThreadX
中的静态字段。是的,你是对的。最好在c-tor和d-tor中使用带有引用计数的singletoin。关键部分在将创建ThreadX并