
最近学习了C++ 多线程之间的通信方式,记录一下。
参考原文:https://blog.csdn.net/eulb/article/details/2177500
C++线程的通信方式有很多种,这里记录一下常用的几种:
1.全局变量
2.互斥量
3.信号量
4.事件
5.临界区
通过全局变量进行通信,要对该变量加关键字volatile
volatile(易变的):每次从内存中去读这个值,而不是因编译器优化从缓存的地方读取
#include <stdio.h>
#include <windows.h>
//全局变量
volatile int signalNum = 0;
DWORD WINAPI threadFuncA(LPVOID lpParamter)
{
Sleep(2000);
if (0 == signalNum)
{
printf("signalNum not changed!\n");
}
else
{
printf("signalNum has changed!\n");
}
return 0;
}
DWORD WINAPI threadFuncB(LPVOID lpParamter)
{
signalNum = 2;
return 0;
}
int main()
{
HANDLE threadA = CreateThread(NULL, 0, threadFuncA, NULL, 0, NULL);
HANDLE threadB = CreateThread(NULL, 0, threadFuncB, NULL, 0, NULL);
WaitForSingleObject(threadA, INFINITE);
CloseHandle(threadA);//CloseHandle只是关闭了系统句柄,该线程还是可以正常的运行
CloseHandle(threadB);
return 0;
}
执行结果:
name为互斥量的名字,也就是说在操作系统中只有一个命名为name的互斥量mutex,如果一个线程得到这个name的互斥锁,其他线程就无法得到这个互斥锁了,必须等待那个线程对这个线程释放。c、当其他线程调用pthread_cond_signal或pthread_cond_broadcast时,会唤醒相应条件变量等待的线程,此时被唤醒的线程,可以参与调度了,此时被唤醒的线程继续执行pthread_cond_wait()函数,函数pthread_cond_wait()返回之前,会重新给条件变量对应的互斥量上锁,在这里就是nready.mutex,若该函数成功返回,则当前线程有重新获得了nready.mutex锁,当然nready.mutex也可能被其他线程继续占有,此时线程再次阻塞。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。
#include <stdio.h>
#include <windows.h>
/*
通过互斥量实现线程间的同步,初始化为没有加锁的状态
*/
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
DWORD WINAPI threadFuncA(LPVOID lpParamter)
{
//对互斥量加锁,如果已经加锁了则等待其解锁,等待时间为INFINITE(表示永久)
WaitForSingleObject(mutex, INFINITE);
/*
WaitForSingleObject(mutex, INFINITE);
这里可以做对共享资源的操作
ReleaseMutex(mutex);
*/
printf("threadFuncA lock mutex,please wait~~~~~~\n");
Sleep(5000);
printf("threadFuncA unlock mutex!!!!!!!!!!!!!!!!\n");
//互斥量解锁
ReleaseMutex(mutex);
return 0;
}
DWORD WINAPI threadFuncB(LPVOID lpParamter)
{
Sleep(2000);
WaitForSingleObject(mutex, INFINITE);
printf("This is threadFuncB ~~~~~~\n");
ReleaseMutex(mutex);
return 0;
}
int main()
{
HANDLE threadA = CreateThread(NULL, 0, threadFuncA, NULL, 0, NULL);
HANDLE threadB = CreateThread(NULL, 0, threadFuncB, NULL, 0, NULL);
WaitForSingleObject(threadB, INFINITE);
CloseHandle(threadA);//CloseHandle只是关闭了系统句柄,该线程还是可以正常的运行
CloseHandle(threadB);
return 0;
}
执行结果:
信号量是最具历史的同步机制。信号量是解决producer/consumer问题的关键要素。对应的MFC类是Csemaphore。Win32函数 CreateSemaphore()用来产生信号量。ReleaseSemaphore()用来解除锁定。Semaphore的现值代表的意义是目前可用的资源数ccriticalsection 多线程 死锁,如果Semaphore的现值为1,表示还有一个锁定动作可以成功。如果现值为5,就表示还有五个锁定动作可以成功。当调用Wait…等函数要求锁定,如果Semaphore现值不为0,Wait…马上返回,资源数减1。当调用ReleaseSemaphore()资源数加1,当时不会超过初始设定的资源总数。
#include <stdio.h>
#include <windows.h>
/*
使用信号量
参数2:当前可用的信号的量个数 范围为(0 - 参数3)
参数3:信号量的最大值
*/
HANDLE signalSemaphore = CreateSemaphore(NULL, 1, 1, NULL);
DWORD WINAPI threadFuncA(LPVOID lpParamter)
{
Sleep(2000);
WaitForSingleObject(signalSemaphore, INFINITE);
printf("threadFuncA 使用了一个信号量!\n");
Sleep(2000);
printf("threadFuncA 释放了一个信号量!\n");
ReleaseSemaphore(signalSemaphore, 1, NULL);
return 0;
}
DWORD WINAPI threadFuncB(LPVOID lpParamter)
{
//这里会使信号量减1
WaitForSingleObject(signalSemaphore, INFINITE);
printf("threadFuncB 使用了一个信号量!\n");
Sleep(5000);
//信号量+1
printf("threadFuncB 释放了一个信号量!\n");
ReleaseSemaphore(signalSemaphore, 1, NULL);
return 0;
}
int main()
{
HANDLE threadA = CreateThread(NULL, 0, threadFuncA, NULL, 0, NULL);
HANDLE threadB = CreateThread(NULL, 0, threadFuncB, NULL, 0, NULL);
WaitForSingleObject(threadA, INFINITE);
CloseHandle(threadA);//CloseHandle只是关闭了系统句柄,该线程还是可以正常的运行
CloseHandle(threadB);
return 0;
}
执行结果:
4.事件
用事件(Event)来同步线程是最具弹性的了。一个事件有两种状态:激发状态和未激发状态。也称有信号状态和无信号状态。事件又分两种类型:手动重置事件和自动重置事件。手动重置事件被设置为激发状态后,会唤醒所有等待的线程,而且一直保持为激发状态,直到程序重新把它设置为未激发状态。自动重置事件被设置为激发状态后,会唤醒“一个”等待中的线程,然后自动恢复为未激发状态。所以用自动重置事件来同步两个线程比较理想。MFC中对应的类为 CEvent.。CEvent的构造函数默认创建一个自动重置的事件ccriticalsection 多线程 死锁,而且处于未激发状态。共有三个函数来改变事件的状态:SetEvent,ResetEvent和PulseEvent。用事件来同步线程是一种比较理想的做法,但在实际的使用过程中要注意的是,对自动重置事件调用SetEvent和PulseEvent有可能会引起死锁,必须小心。
#include <stdio.h>
#include <windows.h>
//通过事件进行通信
/*创建事件CreateEvent
LPSECURITY_ATTRIBUTESlpEventAttributes,// 安全属性
BOOLbManualReset,// 复位方式,如果是TRUE,那么必须用ResetEvent函数来复原到无信号状态。FALSE自动将事件状态复原为无信号状态。
BOOLbInitialState,// 初始状态,TRUE有信号,FALESE无信号
LPCTSTRlpName // 对象名称
*/
HANDLE threadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
DWORD WINAPI threadFuncA(LPVOID lpParamter)
{
printf("threadFuncA 等待事件有信号!\n");
WaitForSingleObject(threadEvent, INFINITE);
printf("threadFuncA 等待事件信号成功,并把事件自动设置为无信号状态!\n");
return 0;
}
DWORD WINAPI threadFuncB(LPVOID lpParamter)
{
Sleep(5000);
//给事件赋予信号
SetEvent(threadEvent);
printf("threadFuncB 给了事件信号!\n");
return 0;
}
int main()
{
HANDLE threadA = CreateThread(NULL, 0, threadFuncA, NULL, 0, NULL);
HANDLE threadB = CreateThread(NULL, 0, threadFuncB, NULL, 0, NULL);
WaitForSingleObject(threadA, INFINITE);
CloseHandle(threadA);//CloseHandle只是关闭了系统句柄,该线程还是可以正常的运行
CloseHandle(threadB);
return 0;
}
执行结果:
CRITICAL_SECTION是最快的。其他内核锁(事件、互斥体),每进一次内核,都需要上千个CPU周期。
上面程序使用@synchronized将draw:方法的方法体修改成同步代码块,该同步代码块的同步监视器是lcaccount对象本声,这样做法符合”加锁→修改→释放锁”的逻辑,任何线程在修改制定资源之前,首先都要对该资源加锁,在加锁期间其他线程无法修改该资源,当该线程修改完成后,释放对该资源的锁定.通过这种方式就可以保证并发线程在任一时刻只有一个线程可以进入修改共享资源的代码区(也称为临界区),所以同一时刻最多只有一个线程处于临界区内,从而保证了线程的安全性.。当某个线程进入临界区内的时候,若其他的某个线程需要访问那段资源,那么就会被排斥,不允许进入,要等到那个已经进入的线程退出临界区后,它才能进入访问,通常用作多线程中访问共享资源时的加锁,避免多线程冲突导致程序崩溃。任一时刻只有一个线程可以拥有临界区对象,拥有临界区的线程可以访问被保护起来的资源或代码段,其他希望进入临界区的线程将被挂起等待,直到拥有临界区的线程放弃临界区时为止,这样就保证了不会在同一时刻出现多个线程访问共享资源。
#include <stdio.h>
#include <afxmt.h>
#include <windows.h>
/*
使用临界区
*/
CCriticalSection cs;
DWORD WINAPI threadFuncA(LPVOID lpParamter)
{
Sleep(2000);
printf("**************threadFuncA 等待临界区解锁!\n");
cs.Lock();
printf("**************threadFuncA 等待临界区解锁成功,对其加锁!\n");
printf("**************threadFuncA 把临界区解锁!\n");
cs.Unlock();
return 0;
}
DWORD WINAPI threadFuncB(LPVOID lpParamter)
{
cs.Lock();
printf("**************threadFuncB 把临界区锁住了!\n");
Sleep(5000);
printf("**************threadFuncB 把临界区解锁!\n");
cs.Unlock();
return 0;
}
int main()
{
HANDLE threadA = CreateThread(NULL, 0, threadFuncA, NULL, 0, NULL);
HANDLE threadB = CreateThread(NULL, 0, threadFuncB, NULL, 0, NULL);
WaitForSingleObject(threadA, INFINITE);
CloseHandle(threadA);//CloseHandle只是关闭了系统句柄,该线程还是可以正常的运行
CloseHandle(threadB);
return 0;
}
执行结果:
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-114831-1.html
留言好多托
找你商量点事呗