摘要:本文主要向大家介绍了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频道!
您输入的评论内容中包含违禁敏感词
我知道了
请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号