线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成。它是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。
线程同步,是指在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
线程互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
线程需要在下面两种情况下互相进行通信:
1.当有多个线程访问共享资源而不使资源被破坏时。
2.当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时。
线程同步的几种方式
线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区,信号量)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系
CreateThread() 创建一个新线程
ExitThread() 正常结束一个线程的执行
TerminateThead() 强制终止一个线程的执行
ResumeThread() 重启一个线程
SuspendThread() 挂起一个线程
GetExiCodeThread() 得到一个线程的退出码
GetThreadPriority() 得到一个线程的优先级
SetThreadPriority() 设置一个线程的优先级
CloseHandle() 关闭一个线程的句柄
CreateRemoteThread() 再另一个进程中创建一个新线程
PostThreadMessage() 发送一条消息给指定的线程
GetCurrentThread() 得到当前的线程句柄
GetCurrentThreadId() 得到当前线程的ID
GetThreadId() 得到指定线程的ID
WaitForSingleObject() 等待单个对象
WaitForMultipleObjects()等待多个对象
临界区(Critical section)
临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。
临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用EnterCriticalSection()和LeaveCriticalSection() 函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection()的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。
#include <windows.h>
#include <string>
#include <iostream>
class CCriticalSectionHelper
{
public:
CCriticalSectionHelper()
{
InitializeCriticalSection(&m_cs);
}
~CCriticalSectionHelper()
{
DeleteCriticalSection(&m_cs);
}
void Lock()
{
EnterCriticalSection(&m_cs);
}
void UnLock()
{
LeaveCriticalSection(&m_cs);
}
private:
CRITICAL_SECTION m_cs;
};
CCriticalSectionHelper lock;
std::string str = "abcd";
DWORD WINAPI ThreadA(LPVOID lp)
{
lock.Lock();
std::cout << "ThreadA Lock:" << str << std::endl;
str = "aaaa";
std::cout << "ThreadA unLock:" << str << std::endl;
lock.UnLock();
return 0;
}
DWORD WINAPI ThreadB(LPVOID lp)
{
lock.Lock();
std::cout << "ThreadB Lock:" << str << std::endl;
str = "bbbb";
std::cout << "ThreadB unLock:" << str << std::endl;
lock.UnLock();
return 0;
}
DWORD WINAPI ThreadC(LPVOID lp)
{
lock.Lock();
std::cout << "ThreadC Lock:" << str << std::endl;
str = "cccc";
std::cout << "ThreadC unLock:" << str << std::endl;
lock.UnLock();
return 0;
}
DWORD WINAPI ThreadD(LPVOID lp)
{
lock.Lock();
std::cout << "ThreadD Lock:" << str << std::endl;
str = "dddd";
std::cout << "ThreadD unLock:" << str << std::endl;
lock.UnLock();
return 0;
}
void main()
{
std::cout << "main begin" << std::endl;
HANDLE theadA = CreateThread(NULL, 0, ThreadA, NULL, 0, NULL);
HANDLE theadB = CreateThread(NULL, 0, ThreadB, NULL, 0, NULL);
HANDLE theadC = CreateThread(NULL, 0, ThreadC, NULL, 0, NULL);
HANDLE theadD = CreateThread(NULL, 0, ThreadD, NULL, 0, NULL);
::WaitForSingleObject(theadA, INFINITE);
::WaitForSingleObject(theadB, INFINITE);
::WaitForSingleObject(theadC, INFINITE);
::WaitForSingleObject(theadD, INFINITE);
std::cout << "main end" << std::endl;
/*
输出:
main begin
ThreadA Lock:abcd
ThreadA unLock:aaaa
ThreadB Lock:aaaa
ThreadB unLock:bbbb
ThreadC Lock:bbbb
ThreadC unLock:cccc
ThreadD Lock:cccc
ThreadD unLock:dddd
main end
*/
}
互斥量(mutex):
采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。 互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。
//创建一个有名和匿名的互斥锁
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // pointer to security attributes
BOOL bInitialOwner, // flag for initial ownership
LPCTSTR lpName // pointer to mutex-object name
);
//lpMutexAttributes // 是否被子进程继承,NULL不能被继承
//bInitialOwner // TRUE, 调用线程拥有这个互斥量,FALSE 调用线程不拥有这个互斥量
//lpName // 互斥量名称,NULL为匿名
//返回值:
//成功:互斥量句柄
//失败:GetLastError function returns ERROR_ALREADY_EXISTS;或 NULL。
//释放互斥量
BOOL ReleaseMutex(
HANDLE hMutex // handle to mutex object
);
//返回值:
//成功:返回非0
//失败:返回0
//获取互斥量
DWORD WaitForSingleObject(
HANDLE hHandle, // handle to object to wait for
DWORD dwMilliseconds // time-out interval in milliseconds。 INFINITE 无限等待
);
通常为了防止进程被重复带起可在程序入口创建互斥量,如下所示:
HANDLE hMutex = NULL;
g_hMutex = CreateMutex(NULL, FALSE, _T("Global\\mutex_name"));
DWORD dwRes = GetLastError();
if (NULL == g_hMutex || ERROR_ALREADY_EXISTS == dwRes)
{
//程序退出
}
//程序执行
多线程中使用互斥量
#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, L"Global\\mutex"); //创建互斥对象(前面加Global\\会是全局的,不在一个session的进程也会有效,目前也不太明白,有了解的大佬欢迎留言)
CreateThread(NULL, 0, ThreadProc1, NULL, 0, NULL);
CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
Sleep(10 * 1000);
system("pause");
return 0;
}
信号量(semaphore)
允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量。
在用 CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过 ReleaseSemaphore()函数将当前可用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。
//头文件 #include <windows.h> //创建信号量API HANDLE WINAPI CreateSemaphore( _In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //指向SECURITY_ATTRIBUTES的指针; _In_ LONG lInitialCount, //信号量对象的初始值; _In_ LONG lMaximumCount, //信号量对象的最大值,这个值必须大于0; _In_opt_ LPCTSTR lpName //信号量对象的名称; ); //打开信号量 可以通过名字跨进程访问 HANDLE OpenSemaphore ( DWORD fdwAccess, //access BOOL bInherithandle, //如果允许子进程继承句柄,则设为TRUE PCTSTR pszName //指定要打开的对象的名字 ); //释放信号量句柄 BOOL WINAPI ReleaseSemaphore( _In_ HANDLE hSemaphore, //信号量对象句柄; _In_ LONG lReleaseCount, //信号量释放的值,必须大于0; _Out_opt_ LPLONG lpPreviousCount //前一次信号量值的指针,不需要可置为空; ); //返回值:成功返回非0; //等待信号量API DWORD WINAPI WaitForSingleObject( _In_ HANDLE hHandle, //信号量对象句柄 _In_ DWORD dwMilliseconds //等待信号量时间,INFINET代表永久等待; ); /* 返回值: WAIT_ABANDONED(0x00000080L) 表示拥有信号量的线程再终止前未释放该信号量; WAIT_OBJECT_0(0x00000000L) 表示等到了信号量; WAIT_TIMEOUT(0x00000102L) 表示等待超时; WAIT_FAILED((DWORD)0xFFFFFFFF) 表示该函数执行失败,用GetLastError()得到错误码; */
事件(Event)
-通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作。
事件(Event)是WIN32提供的最灵活的线程间同步方式,事件可以处于激发状态(signaled or true)或未激发状态(unsignal or false)。
根据状态变迁方式的不同,事件可分为两类:
(1)手动设置:这种对象只可能用程序手动设置,在需要该事件或者事件发生时,采用SetEvent及ResetEvent来进行设置。
(2)自动恢复:一旦事件发生并被处理后,自动恢复到没有事件状态,不需要再次设置。
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // SECURITY_ATTRIBUTES结构指针,可为NULL
BOOL bManualReset, // 手动/自动
// TRUE:表示手动,在WaitForSingleObject后必须手动调用ResetEvent清除信号
// FALSE:表示自动,在WaitForSingleObject后,系统自动清除事件信号
BOOL bInitialState, //初始状态,TRUE为有信号,FALSE为无信号
LPCTSTR lpName //事件的名称
);
//SetEvent是用来设定事件为有信号
SetEvent(
_In_ HANDLE hEvent
);
//ResetEvent设置事件为无信号
ResetEvent(
_In_ HANDLE hEvent
);
使用”事件”机制应注意以下事项:
(1)如果跨进程访问事件,必须对事件命名,在对事件命名的时候,要注意不要与系统命名空间中的其它全局命名对象冲突;
(2)事件是否要自动恢复;
(3)事件的初始状态设置。
#include <windows.h>
#include <iostream>
using namespace std;
HANDLE g_hEvent = NULL;
DWORD WINAPI ThreadA(LPVOID lp)
{
cout << "ThreadA start" << endl;
if (WaitForSingleObject(g_hEvent, 1) == WAIT_OBJECT_0)//有信号,继续执行
{
cout << "ThreadA is working..." << endl;
}
cout << "ThreadA end" << endl;
return 0;
}
DWORD WINAPI ThreadB(LPVOID lp)
{
cout << "ThreadB start" << endl;
if (WaitForSingleObject(g_hEvent, 1) == WAIT_OBJECT_0)//有信号,继续执行
{
cout << "ThreadB is working..." << endl;
}
cout << "ThreadB end" << endl;
return 0;
}
void main()
{
/*
1. bManualReset TRUE:表示手动,在WaitForSingleObject后必须手动调用ResetEvent清除信号
FALSE:表示自动,在WaitForSingleObject后,系统自动清除事件信号
2. bInitialState 初始状态,TRUE为有信号,FALSE为无信号
CreateEventW(
_In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
_In_ BOOL bManualReset,
_In_ BOOL bInitialState,
_In_opt_ LPCWSTR lpName
);
*/
g_hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
HANDLE theadA = CreateThread(NULL, 0, ThreadA, NULL, 0, NULL);
HANDLE theadB = CreateThread(NULL, 0, ThreadB, NULL, 0, NULL);
Sleep(2000);
if (WaitForSingleObject(g_hEvent, 1) == WAIT_OBJECT_0)//有信号,继续执行
{
cout << "Event is signal..." << endl;
}
else
{
cout << "Event not signal..." << endl;
}
Sleep(10000);
//说明:可通过创建时分别设置bManualReset和bInitialState查看输出,以及SetEvent、ResetEvent来查看输出。从而理解event的使用
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。