多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,分为两种类型的多任务处理:基于进程和基于线程

  1)基于进程的多任务处理是程序的并发执行。

  2)基于线程的多任务处理是同一程序的片段的并发执行。

多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。比如说,同一个文件,可能一个线程会对其进行写操作,而另一个线程需要对这个文件进行读操作,可想而知,如果写线程还没有写结束,而此时读线程开始了,或者读线程还没有读结束而写线程开始了,那么最终的结果显然会是混乱的。为了保护共享资源,在线程里也有这么一把锁——互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。

一、创建线程

在Windows下用C++创建线程需要导入windows.h头文件,同时调用CreateThread()函数。如下:

#include <windows.h>
HANDLE thread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

参数说明:

  1)In_opt LPSECURITY_ATTRIBUTES:lpThreadAttributes, {安全设置}

  指向 TSecurityAttributes 结构的指针,一般都是置为NULL,这表示没有访问限制。

  2)In SIZE_T:dwStackSize, {堆栈大小}

  分配给线程的堆栈大小,每个线程都有自己独立的堆栈(也拥有自己的消息队列)。它们都是进程中的内存区域,主要是存取方式不同(栈:先进后出;堆:先进先出)。

  3)In LPTHREAD_START_ROUTINE:lpStartAddress, {入口函数}

  线程入口函数的参数是个无类型指针,用它可以指定任何数据。

  4)In_opt __drv_aliasesMem LPVOID:lpParameter, {函数参数}

  函数指针,新线程建立后将立即执行该函数,函数执行完毕,系统将销毁此线程从而结束多线程的程序。

  5)In DWORD:dwCreationFlags, {启动选项}

  启动选项)有两个可选值:0(线程建立后立即执行入口函数);CREATE_SUSPENDED(线程建立后会挂起等待)。

  6)Out_opt LPDWORD:lpThreadId {输出线程id}

  输出线程句柄ID,注意: 1、线程的 ID 是唯一的;而句柄可能不只一个,比如可以用 GetCurrentThread 获取一个伪句柄、可以用 DuplicateHandle 复制一个句柄等。2、ID 比句柄更轻便,在主线程中 GetCurrentThreadId、MainThreadID获取的都是主线程的 ID。

返回值:线程句柄 ,"句柄" 类似指针,但通过指针可读写对象,通过句柄只是使用对象;有句柄的对象一般都是系统级别的对象(或叫内核对象)。

二、创建互斥量

互斥量和二元信号量类似,资源仅允许一个线程访问。与二元信号量不同的是,信号量在整个系统中可以被任意线程获取和释放,也就是说,同一个信号量可以由一个线程获取而由另一线程释放。而互斥量则要求哪个线程获取了该互斥量锁就由哪个线程释放,其它线程越俎代庖释放互斥量是无效的。

在使用互斥量进行线程同步时会用到以下几个函数:

HANDLE WINAPI CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, //线程安全相关的属性,常置为NULL
BOOL bInitialOwner, //创建Mutex时的当前线程是否拥有Mutex的所有权
LPCTSTR lpName //Mutex的名称
);

其中,lpMutexAttributes也是表示安全的结构,与CreateThread中的lpThreadAttributes功能相同,表示决定返回的句柄是否可被子进程继承,如果为NULL则表示返回的句柄不能被子进程继承。bInitialOwner表示创建Mutex时的当前线程是否拥有Mutex的所有权,若为TRUE则指定为当前的创建线程为Mutex对象的所有者,其它线程访问需要先ReleaseMutex。lpName为Mutex的名称。

DWORD WINAPI WaitForSingleObject(
HANDLE hHandle, //要获取的锁的句柄
DWORD dwMilliseconds //超时间隔
);

其中,WaitForSingleObject的作用是等待一个指定的对象(如Mutex对象),直到该对象处于非占用的状态(如Mutex对象被释放)或超出设定的时间间隔。除此之外,还有一个与它类似的函数WaitForMultipleObjects,它的作用是等待一个或所有指定的对象,直到所有的对象处于非占用的状态,或超出设定的时间间隔。

BOOL WINAPI ReleaseMutex(HANDLE hMutex);

Handle:要等待的指定对象的句柄。dwMilliseconds:超时的间隔,以毫秒为单位;如果dwMilliseconds为非0,则等待直到dwMilliseconds时间间隔用完或对象变为非占用的状态,如果dwMilliseconds 为INFINITE则表示无限等待,直到等待的对象处于非占用的状态。释放所拥有的互斥量锁对象,hMutex为释放的互斥量的句柄。

int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr , int *restrict type);   //获取互斥锁类型
int pthread_mutexattr_settype(const pthread_mutexattr_t *restrict attr , int type); //设置互斥锁类型

参数type表示互斥锁的类型,总共有以下四种类型:
  1)PTHREAD_MUTEX_NOMAL:标准互斥锁,第一次上锁成功,第二次上锁会失败并阻塞;
  2)PTHREAD_MUTEX_RECURSIVE:递归互斥锁,第一次上锁成功,第二次上锁还是会成功,可以理解为内部有一个计数器,每加一次锁计数器加1,解锁减1;
  3)PTHREAD_MUTEX_ERRORCHECK:检查互斥锁,第一次上锁会成功,第二次上锁出错返回错误信息,不会阻塞;
  4)PTHREAD_MUTEX_DEFAULT:默认互斥锁,第一次上锁会成功,第二次上锁会失败。

三、采用互斥锁实现线程同步

#include <windows.h>
#include <iostream> #define NAME_LINE 40 //定义线程函数传入参数的结构体
typedef struct __THREAD_DATA
{
int nMaxNum;
char strThreadName[NAME_LINE]; __THREAD_DATA() : nMaxNum(0)
{
memset(strThreadName, 0, NAME_LINE * sizeof(char));
}
}THREAD_DATA; HANDLE g_hMutex = NULL; //互斥量 //线程函数
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
THREAD_DATA* pThreadData = (THREAD_DATA*)lpParameter; for (int i = 0; i < pThreadData->nMaxNum; ++ i)
{
//请求获得一个互斥量锁
WaitForSingleObject(g_hMutex, INFINITE);
cout << pThreadData->strThreadName << " --- " << i << endl;
Sleep(100);
//释放互斥量锁
ReleaseMutex(g_hMutex);
}
return 0L;
} int main()
{
//创建一个互斥量
g_hMutex = CreateMutex(NULL, FALSE, NULL); //初始化线程数据
THREAD_DATA threadData1, threadData2;
threadData1.nMaxNum = 5;
strcpy_s(threadData1.strThreadName, "线程1");
threadData2.nMaxNum = 10;
strcpy_s(threadData2.strThreadName, "线程2"); //创建第一个子线程
HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, &threadData1, 0, NULL);
//创建第二个子线程
HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, &threadData2, 0, NULL);
//关闭线程
CloseHandle(hThread1);
CloseHandle(hThread2); //主线程的执行路径
for (int i = 0; i < 5; ++ i)
{
//请求获得一个互斥量锁
WaitForSingleObject(g_hMutex, INFINITE);
cout << "主线程 === " << i << endl;
Sleep(100);
//释放互斥量锁
ReleaseMutex(g_hMutex);
} system("pause");
return 0;
}

最新文章

  1. 安装Entity Framework【Setup Entity Framework Environment】(EF基础系列篇4)
  2. vim快捷键总结
  3. Productivity Power Tools 是微软官方推出的 Visual Studio 扩展
  4. JAVA基础知识之NIO.2——Path,Paths,Files
  5. CodeForces 222B Cosmic Tables
  6. ExtJS的MessageBox总结
  7. Web-Scale IT:对企业的影响
  8. 教程:30分钟学会Adobe Premiere
  9. SSIS Package to Call Web Service
  10. Linux格式化字符串
  11. .net 设置版本号信息
  12. 【内网渗透】MSF的exploit和pyload的基础使用
  13. 爬虫免登录进入github
  14. Jmeter多用户利用集合点瞬压并发测试
  15. SpringBoot(5) SpringBoot个性化启动
  16. 不需要再手写 onSaveInstanceState 了,因为你的时间非常值钱
  17. 【bzoj 4764】弹飞大爷
  18. 一目了然了解JAVA集合体系
  19. Dapper/SqlMapper映射对应问题
  20. suoi44 核能显示屏 (cdq分治)

热门文章

  1. gin使用validator库参数校验若干实用技巧
  2. 201871030129-魏琦 实验三 结对项目—《D{0-1}KP 实例数据集算法实验平台》项目报告
  3. 为什么edge AI是一个无需大脑的人
  4. NCF WebApi中 Controller的全解析
  5. 新增秒杀功能、优惠券、支付宝、Docker,newbee-mall升级版开源啦!
  6. SpringCloud Alibaba实战(6:nacos-server服务搭建)
  7. MySQL的自增ID用完了,怎么办?
  8. grep过滤空行和注释行
  9. 【CSAPP】以CTFer的方式打开BufferLab
  10. css初级代码