C++——多线程
1.多进程和多线程:进程是一个总任务,一个进程可能包含多个线程。
2.并行和并发:
并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。
3.共享数据的管理和线程间的通信
1.同步
所谓同步,是指在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。
如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
2.互斥
所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。
如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
3.互锁机制解决多线程的同步问题:只允许一个线程拥有对共享资源的独占
参考:http://www.jizhuomi.com/software/287.html?utm_source=tuicool
在WIN32中,同步机制主要有以下几种:
(1)事件(Event);
(2)信号量(semaphore);
(3)互斥量(mutex);
(4)临界区(Critical section)。
InitializeCriticalSection(&Critical); //初始化临界区对象 hEvent = CreateEvent(NULL, FALSE, TRUE, "event"); hSemaphore = CreateSemaphore(NULL, 1, 100, "sema"); hMutex = CreateMutex(NULL, false, "mutex"); //创建互斥对象
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
2、互斥量:为协调共同对一个共享资源的单独访问而设计的。
3、信号量:为控制一个具有有限数量用户资源而设计。
4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
1. 互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使 用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它。
2. 互斥量(Mutex),信号灯(Semaphore),事件(Event)都可以被跨越进程使用来进行同步数据操作,而其他的对象与数据同步操作无关,但 对于进程和线程来讲,如果进程和线程在运行状态则为无信号状态,在退出后为有信号状态。所以可以使用WaitForSingleObject来等待进程和 线程退出。
3. 通过互斥量可以指定资源被独占的方式使用,但如果有下面一种情况通过互斥量就无法处理,比如现在一位用户购买了一份三个并发访问许可的数据库系统,可以根 据用户购买的访问许可数量来决定有多少个线程/进程能同时进行数据库操作,这时候如果利用互斥量就没有办法完成这个要求,信号灯对象可以说是一种资源计数 器。
1.临界区和互斥量可是视为相同的类型,区别是临界区只能用于进程内,而互斥量可用于不同进程中不不同线程。两个都是将线程串行化,面对高并发和长代码,效率低。
2.信号量:线程并行化。
1. CRITICAL_SECTION(临界区): 公厕管理人员每次允许一个人进入,直到他出去了,下一个人才可以进入。
2. Event(事件): 他告诉你公厕里面当前的状态。但是,你可以闯进去。他不会管你。要干什么取决于你。
3. Semaphore(信号量): 他允许公厕里面有N个人同时用,再多的人就必须排队。
4. mutex(互斥量): 厕所是属于他的。他用的时候,别人决不能进去。他不用的时候,得到他的允许,别人才能进去。他也可以选择让厕所空着。
原文:https://blog.csdn.net/liangtianmeng/article/details/81282486
应用:
一、并行方式的信号量在访问相同的一组资源时是最好的方法,因为它最大限度减少了系统调度线程的成本。
二、临界区和互斥量只应用于访问串行资源(例如使用全局计数器,系统参数访问和修改)。同一进程下的线程串行化时,只应该使用临界区。
三、按指定的规则进行线程协调时使用事件。
1.临界区(Critical Section)
临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。
#include "stdafx.h" #include<windows.h> #include<iostream> using namespace std; int number = 1; //定义全局变量 CRITICAL_SECTION Critical; //定义临界区句柄 unsigned long __stdcall ThreadProc1(void* lp) { while (number < 100) { EnterCriticalSection(&Critical);//标识一个临界区。 cout << "thread 1 :"<<number << endl; ++number; _sleep(100); LeaveCriticalSection(&Critical);//释放一个临界区 } return 0; } unsigned long __stdcall ThreadProc2(void* lp) { while (number < 100) { EnterCriticalSection(&Critical); cout << "thread 2 :"<<number << endl; ++number; _sleep(100); LeaveCriticalSection(&Critical); } return 0; } int main() { InitializeCriticalSection(&Critical); //初始化临界区对象 CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); Sleep(10*1000); system("pause"); return 0; }
2.事件(Event)
事件(Event)是WIN32提供的最灵活的线程间同步方式,事件可以处于激发状态(signaled or true)或未激发状态(unsignal or false)。根据状态变迁方式的不同,事件可分为两类:
(1)手动设置:这种对象只可能用程序手动设置,在需要该事件或者事件发生时,采用SetEvent及ResetEvent来进行设置。 //SetEvent把指定的事件对象设置为有信号状态,ResetEvent把指定的事件对象设置为无信号状态
(2)自动恢复:一旦事件发生并被处理后,自动恢复到没有事件状态,不需要再次设置。
使用”事件”机制应注意以下事项:
(1)如果跨进程访问事件,必须对事件命名,在对事件命名的时候,要注意不要与系统命名空间中的其它全局命名对象冲突;
(2)事件是否要自动恢复;
(3)事件的初始状态设置。
#include "stdafx.h" #include<windows.h> #include<iostream> using namespace std; int number = 1; //定义全局变量 HANDLE hEvent; //定义事件句柄 unsigned long __stdcall ThreadProc1(void* lp) { while (number < 100) { WaitForSingleObject(hEvent, INFINITE); //等待对象为有信号状态 cout << "thread 1 :"<<number << endl; ++number; _sleep(100); SetEvent(hEvent);//将事件对象置为有信号状态 } return 0; } unsigned long __stdcall ThreadProc2(void* lp) { while (number < 100) { WaitForSingleObject(hEvent, INFINITE); //等待对象为有信号状态 cout << "thread 2 :"<<number << endl; ++number; _sleep(100); SetEvent(hEvent);//将事件对象置为有信号状态 } return 0; } int main() { CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); hEvent = CreateEvent(NULL, FALSE, TRUE, "event"); Sleep(10*1000); system("pause"); return 0; }
3.信号量
信号量是维护0到指定最大值之间的同步对象。信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的。信号量对象在控制上可以支持有限数量共享资源的访问。
信号量的特点和用途可用下列几句话定义:
(1)如果当前资源的数量大于0,则信号量有效;
(2)如果当前资源数量是0,则信号量无效;
(3)系统决不允许当前资源的数量为负值;
(4)当前资源数量决不能大于最大资源数量。
//创建信号量 HANDLE CreateSemaphore ( PSECURITY_ATTRIBUTE psa, //信号量的安全属性 LONG lInitialCount, //开始时可供使用的资源数 LONG lMaximumCount, //最大资源数 PCTSTR pszName); //信号量的名称 //释放信号量 BOOL WINAPI ReleaseSemaphore( HANDLE hSemaphore, //要增加的信号量句柄 LONG lReleaseCount, //信号量的当前资源数增加lReleaseCount LPLONG lpPreviousCount //增加前的数值返回 ); //打开信号量 HANDLE OpenSemaphore ( DWORD fdwAccess, //access BOOL bInherithandle, //如果允许子进程继承句柄,则设为TRUE PCTSTR pszName //指要打开的对象的名字 );
#include "stdafx.h" #include<windows.h> #include<iostream> using namespace std; int number = 1; //定义全局变量 HANDLE hSemaphore; //定义信号量句柄 unsigned long __stdcall ThreadProc1(void* lp) { long count; while (number < 100) { WaitForSingleObject(hSemaphore, INFINITE); //等待信号量为有信号状态 cout << "thread 1 :"<<number << endl; ++number; _sleep(100); ReleaseSemaphore(hSemaphore, 1, &count); } return 0; } unsigned long __stdcall ThreadProc2(void* lp) { long count; while (number < 100) { WaitForSingleObject(hSemaphore, INFINITE); //等待信号量为有信号状态 cout << "thread 2 :"<<number << endl; ++number; _sleep(100); ReleaseSemaphore(hSemaphore, 1, &count); } return 0; } int main() { hSemaphore = CreateSemaphore(NULL, 1, 100, "sema"); CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); Sleep(10*1000); system("pause"); return 0; }
4.互斥量/互锁
采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。
eg1:
#include "stdafx.h" #include<windows.h> #include<iostream> using namespace std; int number = 1; //定义全局变量 HANDLE hMutex; //定义互斥对象句柄 unsigned long __stdcall ThreadProc1(void* lp) { while (number < 100) { WaitForSingleObject(hMutex, INFINITE); cout << "thread 1 :"<<number << endl; ++number; _sleep(100); ReleaseMutex(hMutex); } return 0; } unsigned long __stdcall ThreadProc2(void* lp) { while (number < 100) { WaitForSingleObject(hMutex, INFINITE); cout << "thread 2 :"<<number << endl; ++number; _sleep(100); ReleaseMutex(hMutex); } return 0; } int main() { hMutex = CreateMutex(NULL, false, "mutex"); //创建互斥对象 CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL); CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL); Sleep(10*1000); system("pause"); return 0; }
eg2:
#include <iostream> #include <windows.h> using namespace std; HANDLE hMutex; DWORD WINAPI Fun(LPVOID lpParamter)//线程定义 { while (1) { WaitForSingleObject(hMutex, INFINITE);//申请得到该资源:hMutex指定所申请的资源的句柄,INFINITE,表示如果没有申请到资源就一直等待该资源 cout << "Fun display!" << endl; Sleep(1000); ReleaseMutex(hMutex);//该函数用于释放一个独占资源,进程一旦释放该资源,该资源就不再属于它了 } } int main() { HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);//开启线程 hMutex = CreateMutex(NULL, FALSE, L"screen"); //创造了一个名为screen并且归属于创建它的进程的资源 CloseHandle(hThread); while (1) { WaitForSingleObject(hMutex, INFINITE);//申请得到该资源:hMutex指定所申请的资源的句柄,INFINITE,表示如果没有申请到资源就一直等待该资源 cout << "main display!" << endl; Sleep(2000); ReleaseMutex(hMutex);//该函数用于释放一个独占资源,进程一旦释放该资源,该资源就不再属于它了 } return 0; }
---------------------
作者:已不再少年
来源:CSDN
原文:https://blog.csdn.net/s_lisheng/article/details/74278765
版权声明:本文为博主原创文章,转载请附上博文链接!