线程同步,同步的意思并不是一同、一起做某些事,同的意思应该是协同,互相配合。也就是说要有一定的调理或者规矩的执行,比如:我先干完,你在干。
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其它线程也不能调用这个方法。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。例如Window API函数SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的LRESULT值返回给调用者。——来自百度百科。
下面就是解决问题的方法
1.原子访问
因为CPU运算时从寄存器中读取数据比从内存中读取数据快得多,所以代码在编译阶段编译器往往会对代码进行优化,使CPU读取你代码中的变量时,从寄存器中读取而不是内存中,从而提高运行速度。但是在多线程中就会出现问题,比如:当有两个线程A、B都要用寄存器的变量,并且它们要对这个变量进行修改,这就有可能导致线程A在还没有对变量修改完成,线程B就又拿到这个变量,所以B拿到的更新之前的变量,就会导致整个运算结果出现问题。
原子访问,用关键字volatile声明变量,volatile可以防止编译优化,使每次的读取操作都从内存中读取,就可以大幅的减少内存一致性错误,让B线程可以看到A线程对变量操作后的结果。
volatile int value;
2.关键段(临界区)
例如:同样有两个线程A、B都执行同一段代码,value的初始值为1
value--; cout << value;
那么线程A和线程B输出的结果可能是一样的也可能是不同的,如果线程A刚拿到value的值还没有执行value--操作,时间片就到了,这时线程B开始执行,输出结果为0,等线程A重新获得时间片继续执行时,直接执行value--,输出的结果也是0,这并不是我们想得到的结果,我们往往要这个变量每次执行这段代码都-1,我们就可以要使用关键段来控制线程执行这段代码。
关键段的声明
CRITICAL_SECTION m_cs;关键段的初始化,声明了一个关键段就必须对它进行初始化
InitializeCriticalSection(&m_cs);进入关键段和离开关键段
EnterCriticalSection(&m_cs); //进入关键段 value--; cout << value; LeaveCriticalSection(&m_cs); //离开关键段
这样只要有线程进入了关键段,其他的线程就必须在关键段外等待,直到关键段中的线程离开。但是如果关键段中代码需要执行很长时间,其他的线程就一直在关键段外被阻塞,就会降低整个程序的执行效率,这时我们可以使用旋转锁或者TryEnterCriticalSection函数来解决这个问题。
3.互斥量
互斥量也可以做到和关键段同样的效果
互斥量是内核对象,可以跨进程使用。
(1).声明
HANDLE m_Mutex;
(2).初始化
函数原型
CreateMutexW( _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, //安全描述符,如果为空则为用默认的安全描述符 _In_ BOOL bInitialOwner, //如果此值为TRUE并且调用者创建了互斥锁,则调用线程获得互斥锁对象的初始所有权。否则,调用线程不会获得互斥锁的所有权 _In_opt_ LPCWSTR lpName //跨进程时的进程名 );
m_Mutex = CreateMutex(NULL, FALSE, NULL);(3).使用WaitForSingleObject来等待互斥量的状态
if (WAIT_TIMEOUT == WaitForSingleObject(m_Mutex, 100)) continue;
意为等待这个互斥量100毫秒,如果这个互斥量还有没变为有信号状态则返回。这样就可以控制只有一个线程进入下面的代码,知道这个线程释放互斥量。
(4).释放互斥量
ReleaseMutex(m_Mutex);
4.事件
和互斥量的用法差不多 声明、初始化、等待、释放
HANDLE m_Event; m_Event = CreateEvent(NULL, FALSE, TRUE, NULL); if (WAIT_TIMEOUT == WaitForSingleObject(m_Event, 100)) continue; SetEvent(m_Event); //设置事件为有信号
5.信号量
信号量、互斥量、事件都是内核对象可以跨进程使用,但信号量的初始化和释放时稍有不同,它可以控制进入关键区域的线程个数。
HANDLE m_hSemaphore; m_hSemaphore = CreateSemaphore(NULL, 1, 10, NULL); if (WAIT_TIMEOUT == WaitForSingleObject(m_hSemaphore, 100)) continue; ReleaseSemaphore(m_hSemaphore, 1, NULL);
CreateSemaphore中第三个参数是创建信号的最大数。第二个参数是创建后就释放的信号量数量。
ReleaseSemaphore中第二个参数是要释放的信号量数量。第三个参数是返回释放前释放的信号量数目,NULL则不接受。