VC编程之线程,进程间的通讯和同步原理,实现用例和应用
小标 2018-12-14 来源 : 阅读 860 评论 0

摘要:本文主要向大家介绍了VC编程之线程,进程间的通讯和同步原理,实现用例和应用,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。

本文主要向大家介绍了VC编程之线程,进程间的通讯和同步原理,实现用例和应用,通过具体的内容向大家展示,希望对大家学习VC编程有所帮助。

VC编程之线程,进程间的通讯和同步原理,实现用例和应用

线程/进程间的通讯方式
—使用全局变量/共享内存
—使用thread中的lParam参数
—使用socket
—使用窗口和消息
—使用命名管道/匿名管道
—使用cmd参数
—使用environment变量

线程的启动,退出和lParam参数通讯
[cpp] view plain copy
1.VC:  
2.  
3.#include   
4.  
5.DWORD WINAPI ThreadProc(LPVOID lParam);  
6.  
7.DWORD dwThreadId;  
8.HANDLE hThread = CreateThread(NULL, 0, ThreadProc, lParam, 0, &dwThreadId);  
9.  
10.::TerminateThread(hThread, 0) //杀死线程,强烈不推荐使用!  
11.::WaitForSingleObject(hThread, INFINITE) //等待线程退出  
12.::CloseHandle(hThread); //不再关心该线程  
13.::GetExitCodeThread (); //获取线程退出代码  

[csharp] view plain copy
1..Net:  
2.  
3.Using System.Threading;  
4.  
5.Static void ThreadProc(Object lParam)  
6.  
7.Object lParam = null;  
8.Thread th = new Thread(new ParameterizedThreadStart(ThreadProc));  
9.th.Start(lParam);  
10.  
11.th.IsBackground = true; // 主循环结束后依靠.Net运行机制自动取消该线程  
12.th.Join(); //等待线程退出  
13.th.Abort(); //杀死线程  

进程的启动,退出,命令行和环境变量
[cpp] view plain copy
1.VC:  
2.  
3.#include   
4.  
5.STARTUPINFO si = sizeof(STARTUPINFO) };  
6.PROCESS_INFORMATION ps;  
7.  
8.Char* pFileName = …;  
9.Char* pArgs = …;  
10.LPVOID pEnv = NULL;  
11.BOOL bRet = CreateProcess(pFileName, pArgs, …, pEnv, NULL, &si, &ps);  
12.  
13.::TerminateProcess(ps.hProcess, 0) //杀死进程,不推荐使用,但比TerminateThread强很多。  
14.::WaitForSingleObject(ps.hProcess, INFINITE) //等待进程退出  
15.::CloseHandle(ps.hProcess); // 不再关心该进程  
16.::GetExitCodeProcess(); //获取进程退出代码  

[csharp] view plain copy
1..Net:  
2.  
3.Using System.Diagnotics;  
4.  
5.Process p = new Process();  
6.p.StartInfo = new ProcessStartInfo();  
7.p.StartInfo.FileName = …;  
8.p.StartInfo.Arguments = …;  
9.p.StartInfo.EnvironmentVariables = …;  
10.p.Start();  
11.  
12.p.WaitForExit(); //等待进程退出  
13.p.Kill(); //杀死进程  
14.p.ExitCode // 获取进程退出代码  

管道通讯
[cpp] view plain copy
1.创建匿名管道(读管道和写管道)  
2.BOOL WINAPI CreatePipe(  
3.        PHANDLE hReadPipe,    
4.        PHANDLE hWritePipe,   
5.        LPSECURITY_ATTRIBUTES lpPipeAttributes,  
6.        DWORD nSize);   
7.  
8.销毁匿名管道  
9.BOOL WINAPI CloseHandle(  
10.        HANDLE hPipe  
11.    );  
12.  
13.管道通讯用例NPSample  

共享内存(VC)
[cpp] view plain copy
1.CreateFileMapping  
2.MapViewOfFile  
3.UnmapViewOfFile  
4.CloseHandle  
5.共享内存实现用例ShmSample  

线程/进程间的同步
[cpp] view plain copy
1.有了以上进程通讯的方式,必然会产生同步问题。  
2.同步的几种方式:  
3.    临界区CriticalSection --- 轻量级代码关键段  
4.    互斥锁Mutex --- 互斥锁,只能被一个进程拥有  
5.    信号量Semaphore  --- 信号灯,  
6.   事件Event    ---- 一次性手动或自动事件  
7.   原子变量Atomic   ---- 保证一个变量值唯一性  
8.   自旋锁SpinLock  ---- 用户态自旋锁,适合短代码加锁  
9.     
10.   都可以跨进程使用,但临界区跨进程必须放于共享内存中。  

CriticalSection
[cpp] view plain copy
1.VC:  
2.  
3.#include   
4.  
5.CRITICAL_SECTION sec;  
6.  
7.InitializeCriticalSection(&sec); //初始化临界区  
8.InitializeCriticalSectionAndSpinCount(&sec, 2000); //自旋方式初始化临界区  
9.DeleteCriticalSection(&sec); //删除临界区  
10.EnterCritcalSection(&sec); //进入临界区  
11.LeaveCriticalSection(&sec); //离开临界区  
12.TryEnterCriticalSection(&sec); //试图进入临界区,进入成功要离开,否则返回FALSE  
13.  
14.注意:尽量使用类锁(自动析构)如有性能或者防止死锁需要,尽量不使用return,防止离开时忘记释放锁  

[csharp] view plain copy
1..Net:  
2.  
3.String s;  
4.lock (s) // 锁定对象s  
5.{  
6.    ….;  
7.}  
8.  
9.System.Threading.Motitor.Enter(s);  
10.  
11.System.Threading.Motitor.Leave(s); //尽量加到finally块里面,避免抛出异常导致锁未释放  

Mutex
[cpp] view plain copy
1.VC:  
2.  
3.#include   
4.  
5.HANDLE mutex;  
6.  
7.mutex = CreateMutex(NULL,…); //初始化匿名互斥锁  
8.Mutex = CreateMutex(“123”, …); //初始化有名称互斥锁  
9.Mutex = OpenMutex(“123”, …); //打开有名称互斥锁  
10.CloseHandle(mutex); //关闭互斥锁;  
11.WaitForSingleObject(mutex, waitTime); // 等待互斥锁,第二个参数为等待时间  
12.ReleaseMutex(mutex); //释放互斥锁;  
13.注意:尽量使用类锁(自动析构)如有性能或者防止死锁需要,尽量不使用return,防止离开时忘记释放锁  
14.注意:线程/进程退出时互斥锁将自动被释放,其他等待的线程/进程将获得该锁并返回WAIT_ABANDONED.  

[csharp] view plain copy
1..Net:  
2.  
3.Using System.Threading;  
4.  
5.Mutex m = new Mutex();  
6.Mutex m = new Mutex(“123”, …); //初始化有名称互斥锁  
7.m.WaitOne(); // 等待互斥锁  
8.m.ReleaseMutex(); // 释放互斥锁,尽量加到finally块里面,避免抛出异常导致锁未释放  

Semaphore
[cpp] view plain copy
1.VC:  
2.  
3.#include   
4.  
5.HANDLE sem;  
6.  
7.sem = CreateSemaphore(NULL,…); //初始化匿名信号量并初始化初始信号数量  
8.sem = CreateSemaphore(“123”, …); //初始化有名称信号量  
9.sem = OpenSemaphore(“123”, …); //打开有名称信号量  
10.CloseHandle(sem); //关闭信号量;  
11.WaitForSingleObject(sem, waitTime); // 等待信号量,第二个参数为等待时间,若成功,信号量计数-1  
12.ReleaseSemaphore(sem, count); //将信号量计数增加count;  
13.注意:尽量使用类锁(自动析构)如有性能或者防止死锁需要,尽量不使用return,防止离开时忘记释放锁  
14.注意:线程/进程退出时信号量将不会被释放,其他等待的线程/进程将依然会锁住  

[csharp] view plain copy
1..Net:  
2.  
3.Using System.Threading;  
4.  
5.Semaphore m = new Semaphore();  
6.Semaphore m = new Semaphore(“123”, …); //初始化有名称信号量  
7.m.WaitOne(); // 等待信号量并将信号量数目-1  
8.m.Release (count); // 信号量计数增加count,尽量加到finally块里面,避免抛出异常导致锁未释放  

利用Semaphore实现控制队列
—Semaphore和queue一起用,可完成完整的加锁队列,

—消费者每次取数据时等待信号量,等待成功后取数据(加锁)。
—生产者每次生产数据时先将数据入队(加锁),并将信号量计数+1。

Event
[cpp] view plain copy
1.VC:  
2.  
3.#include   
4.  
5.HANDLE eve;  
6.  
7.eve = CreateEvent(NULL,…); //初始化匿名事件并初始化状态和工作方式  
8.eve = CreateEvent(“123”, …); //初始化有名称事件  
9.eve = OpenEvent(“123”, …); //打开有名称事件  
10.CloseHandle(eve); //关闭事件;  
11.WaitForSingleObject(eve, waitTime); // 等待事件,第二个参数为等待时间,若成功,如工作方式为自动重置,事件将自动被重置  
12.SetEvent(eve); // 设置事件为有信号状态  
13.ResetEvent(eve); // 设置事件为无信号状态  
14.注意:尽量使用类锁(自动析构)如有性能或者防止死锁需要,尽量不使用return,防止离开时忘记释放锁  
15.注意:线程/进程退出时信号量将不会被释放,其他等待的线程/进程将依然会锁住  

[csharp] view plain copy
1..Net:  
2.  
3.Using System.Threading;  
4.  
5.EventHandle m = new EventHandle ();  
6.EventHandle m = new EventHandle (“123”, …); //初始化有名称信号量  
7.m.WaitOne(); // 等待信号量并将信号量数目-1  
8.m.Release (count); // 信号量计数增加count,尽量加到finally块里面,避免抛出异常导致锁未释放  

利用Event控制线程运行(VC)
[cpp] view plain copy
1.看过很多线程代码是这样写的  
2.DWORD WINAPI ThreadProc(LPVOID param)  
3.{  
4.    while (m_bRun)  
5.    {  
6.        DoWork(…);  
7.        Sleep(1);  
8.       }  
9.}  
10.Void Stop()  
11.{  
12.    m_bRun = FALSE;  
13.    ::WaitForSingleObject(…);  
14.}  

[cpp] view plain copy
1.实际应该这样优化:  
2.DWORD WINAPI ThreadProc(LPVOID param)  
3.{  
4.    while (TRUE)  
5.    {  
6.    DWORD dwRet = WaitForSingleObject(hEvent,   1);  
7.    if (dwRet == WAIT_OBJECT_0)  
8.        break;  
9.    DoWork(…);  
10.    Sleep(1);  
11.       }  
12.}  
13.  
14.Void Stop()  
15.{  
16.    SetEvent(hEvent);  
17.    ::WaitForSingleObject(…);  
18.}  

原子变量
—原子变量的原理
原子变量的原理是利用硬件支持锁定某块内存的功能实现,就算有多个CPU同时访问该段内存,也只有一个能进入该内存,其他CPU将被锁住。
由于原子变量并非对代码段加锁,而是对数据区加锁,并且锁的空间很小,因此一般只适合数量上的(引用计数)或者数值上的(个数,次数)的加锁。
VC: InterlockedIncrement, InterlockedExchangeAdd, InterlockedDecrement, …
.Net: System.Threading.Interlocked类

自旋锁
—自旋锁分为用户态锁和内核态锁,利用锁定某块内存的方式不断读取该块内存数据来加锁/解锁,工作机理和原子变量类似,但要注意自旋锁仅适合非单核CPU(单核在用户态自旋是没有意义的)和较短代码段的锁,若锁的时间过长将引起大量的CPU耗损。
参考:大并发下的高性能编程 – 改进的(用户态)自旋锁

同步与死锁
—死锁原因
—忘记在某个地方释放锁
—使用TerminateThread/TerminateProcess导致锁对象未释放
—加锁未按顺序加锁
—锁太多,不知该如何加锁

—每个锁的锁周期要短,不要对非关键代码区域段加锁
—每个锁的目的要明确,不要一个锁去锁太多的对象和元素
—加锁要按顺序加锁
—注意SendMessage类函数和回调函数的加锁,确保在调用之前已经释放了应该释放的锁    

以上就介绍了VC/MFC的学习,希望对VC/MFC有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言VC/MFC频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程