1、为什么线程要同步?

#include<windows.h>
#include<iostream>
using namespace std;
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{cout << "线程1:创建成功!" << endl;return 0;
}
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{cout << "线程2:创建成功!" << endl;return 0;
}
DWORD WINAPI ThreadFunc3(PVOID pvParam)
{cout << "线程3:创建成功!" << endl;return 0;
}
int main(int argc, char* argv[])
{HANDLE ThreadHandle1, ThreadHandle2, ThreadHandle3;ThreadHandle1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL);ThreadHandle2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL);ThreadHandle3 = CreateThread(NULL, 0, ThreadFunc3, NULL, 0, NULL);getchar();return 0;
}


举一个例子,创建三个线程。根据结果得知:操作系统随机调用线程,不能预知线程的执行顺序。
2、Windows线程同步方法主要有临界区、事件、互斥量、信号量四种。
3、临界区

临界区—相关API函数
定义临界区对象(通常全局变量)
CRITICAL_SECTION cs
临界区对象初始化
InitializeCriticalSection(&cs)
进入临界区
EnterCriticalSection(&cs)
离开临界区
LeaveCriticalSection(&cs)
释放临界区对象
DeleteCriticalSection(&cs)

利用临界区相关API函数,上述程序可以修改为:

#include<windows.h>
#include<iostream>
using namespace std;
CRITICAL_SECTION cs;
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{EnterCriticalSection(&cs);cout << "线程1:创建成功!" << endl;LeaveCriticalSection(&cs);return 0;
}
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{EnterCriticalSection(&cs);cout << "线程2:创建成功!" << endl;LeaveCriticalSection(&cs);return 0;
}
DWORD WINAPI ThreadFunc3(PVOID pvParam)
{EnterCriticalSection(&cs);cout << "线程3:创建成功!" << endl;LeaveCriticalSection(&cs);return 0;
}
int main(int argc, char* argv[])
{InitializeCriticalSection(&cs);HANDLE ThreadHandle1, ThreadHandle2, ThreadHandle3;ThreadHandle1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL);ThreadHandle2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL);ThreadHandle3 = CreateThread(NULL, 0, ThreadFunc3, NULL, 0, NULL);getchar();DeleteCriticalSection(&cs);return 0;
}


虽然临界区同步速度很快,但是只能用来同步本进程内的线程,不能同步多个进程中的线程。
4、两个重要的同步等待函数

DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds        0:立即返回;INFINITE:永远等待);
返回值:
WAIT_OBJECT_0   线程等待的对象变为已通知状态
WAIT_TIMEOUT    超时
WAIT_FAILED     将无效的句柄传递给WaitForSingleObjectDWORD WaitForMultipleObject(DWORD dwCount,              指明内核对象的数量CONST HANDLE* phHandle,        指向内核对象句柄的数组指针BOOL fWaitAll,             true:所有对象变为已通知前,不允许调用线程DWORD dwMilliseconds);

上述的程序代码又可以改写为:

#include<windows.h>
#include<iostream>
using namespace std;
HANDLE ThreadHandle1, ThreadHandle2, ThreadHandle3;
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{cout << "线程1:创建成功!" << endl;return 0;
}
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{WaitForSingleObject(ThreadHandle1, INFINITE);cout << "线程2:创建成功!" << endl;return 0;
}
DWORD WINAPI ThreadFunc3(PVOID pvParam)
{WaitForSingleObject(ThreadHandle2, INFINITE);cout << "线程3:创建成功!" << endl;return 0;
}
int main(int argc, char* argv[])
{ThreadHandle1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL);ThreadHandle2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL);ThreadHandle3 = CreateThread(NULL, 0, ThreadFunc3, NULL, 0, NULL);getchar();return 0;
}


线程1直接创建,线程2等待线程1创建,线程3等待线程2创建。
5、互斥量

互斥量—API函数
互斥量的创建,返回句柄
HANDLE CreateMutex(PSECURITY_ATTRIBUTES psa,    安全属性的指针BOOL bInitialOwner,          初始化互斥对象的所有者PCTSTR pszName               指向互斥对象名的指针);
为现有的一个已命名互斥对象创建一个新句柄
HANDLE openMutex(DOWRD fdwAccess,BOOL bInheritHandle,PCTSTR pszName);
释放互斥量
HANDLE ReleaseMutex(HANDLE hMutex);
等待互斥量
DOWRD WaitForSingleObject(HANDLE hHandle,DOWRD dwMilliseconds);

上述程序又可以改写为:

#include<windows.h>
#include<iostream>
using namespace std;
HANDLE ThreadHandle1, ThreadHandle2, ThreadHandle3;
HANDLE ThreadMutex;
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{WaitForSingleObject(ThreadMutex, INFINITE);cout << "线程1:创建成功!" << endl;ReleaseMutex(ThreadMutex);return 0;
}
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{WaitForSingleObject(ThreadMutex, INFINITE);cout << "线程2:创建成功!" << endl;ReleaseMutex(ThreadMutex);return 0;
}
DWORD WINAPI ThreadFunc3(PVOID pvParam)
{WaitForSingleObject(ThreadMutex, INFINITE);cout << "线程3:创建成功!" << endl;ReleaseMutex(ThreadMutex);return 0;
}
int main(int argc, char* argv[])
{ThreadMutex = CreateMutex(NULL, false, "tMutex");ThreadHandle1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL);ThreadHandle2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL);ThreadHandle3 = CreateThread(NULL, 0, ThreadFunc3, NULL, 0, NULL);getchar();return 0;
}


注意CreateMutex第二个参数为false,说明当前主线程并不拥有互斥对象。
6、事件

事件—API函数
创建事件内核对象,返回句柄
HANDLE CreateEvent(PSECURITY_ATTRIBUTES psa,        安全属性BOOL fManuaResrt,               复位方式,false自动BOOL fInitialState,             初始状态,true有信号PCTSTR pszName                   对象名称);
打开一个已经存在的命名事件对象
HANDLE OpenEvent(DOWRD fdwAccess,BOOL fInherit,PCTSTR pszName);
EVENT_ALL_ACCESS    要求对事件对象进行完全访问
EVENT_MODIFY_STATE  允许SetEvent和ResetEvent函数
SYNCHRONIZE         允许事件对象的使用同步
将事件设置为已通知状态
BOOL SetEvent(HANDLE hEvent);
将事件设置为未通知状态
BOOL ResetEvent(HANDLE hEvent);

上述程序可以改为:

#include<windows.h>
#include<iostream>
using namespace std;
HANDLE ThreadHandle1, ThreadHandle2, ThreadHandle3;
HANDLE ThreadEvent;
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{WaitForSingleObject(ThreadEvent, INFINITE);cout << "线程1:创建成功!" << endl;SetEvent(ThreadEvent);return 0;
}
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{WaitForSingleObject(ThreadEvent, INFINITE);cout << "线程2:创建成功!" << endl;SetEvent(ThreadEvent);return 0;
}
DWORD WINAPI ThreadFunc3(PVOID pvParam)
{WaitForSingleObject(ThreadEvent, INFINITE);cout << "线程3:创建成功!" << endl;SetEvent(ThreadEvent);return 0;
}
int main(int argc, char* argv[])
{ThreadEvent = CreateEvent(NULL, false, true, "tEvent");ThreadHandle1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL);ThreadHandle2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL);ThreadHandle3 = CreateThread(NULL, 0, ThreadFunc3, NULL, 0, NULL);getchar();return 0;
}


注意CreateEvent中的第三个参数要设置为true,一开始就是有信号的。
其中,每个线程函数还可以改写为:

DWORD WINAPI ThreadFunc1(PVOID pvParam)
{HANDLE event1=OpenEvent(EVENT_ALL_ACCESS,true,"tEvent");WaitForSingleObject(event1,INFINITE);cout<<"线程1:创建成功!"<<endl;SetEvent(ThreadEvent);return 0;
}

7、信号量

信号量—API函数
创建信号量
HANDLE CreateSemaphore(PSECURITY_ATTRIBUTES psa,LONG lInitialCount,             当前可用资源数LONG lMaximumCount,              允许最大资源数PCTSTR pszName);
为现有的一个已命名信号机对象创建一个新句柄
HANDLE OpenSemaphore(DWORD fdwAccess,BOOL bInheritHandle,PCTSTR pszName);
SEMAPHORE_ALL_ACCESS        要求对信号量的完全访问
SEMAPHORE_MODIFY_STATE      允许使用ReleaseSemaphore函数
SYNCHRONIZE                 允许使用信号量同步
释放信号量
ReleaseSemaphore(HANDLE hSem,LONG lReleaseCount,PLONG plPreviousCount);

上述程序可以改写为:

#include<windows.h>
#include<iostream>
using namespace std;
HANDLE ThreadHandle1, ThreadHandle2, ThreadHandle3;
HANDLE ThreadSemaphore;
LONG a = 0;
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{WaitForSingleObject(ThreadSemaphore, INFINITE);cout << "线程1:创建成功!" << endl;ReleaseSemaphore(ThreadSemaphore, 1, &a);return 0;
}
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{WaitForSingleObject(ThreadSemaphore, INFINITE);cout << "线程2:创建成功!" << endl;ReleaseSemaphore(ThreadSemaphore, 1, &a);return 0;
}
DWORD WINAPI ThreadFunc3(PVOID pvParam)
{WaitForSingleObject(ThreadSemaphore, INFINITE);cout << "线程3:创建成功!" << endl;ReleaseSemaphore(ThreadSemaphore, 1, &a);return 0;
}
int main(int argc, char* argv[])
{ThreadSemaphore = CreateSemaphore(NULL, 1, 3, NULL);ThreadHandle1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL);ThreadHandle2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL);ThreadHandle3 = CreateThread(NULL, 0, ThreadFunc3, NULL, 0, NULL);getchar();return 0;
}


注意CreateSemaphore中的参数设置,为了防止乱序产生,当前可用的资源数应该设置为1。
8、总结
互斥量、事件、信号量都是内核对象,可用于进程间的线程同步;临界区只能用于进程内的线程同步。虽然在同一个进程内同步时,互斥量和临界区的功能相似,但是临界区的性能更好。
事件和其他几个同步方法的不同在于事件的主要作用不是保护共享资源,而是用于等待某个事件和在特定的事件发生时的发送信号,以协调线程之间的动作。
信号量与其他同步方法的区别在于它允许一个以上的线程同时访问共享资源,而其他线程同步方法都保证同时只能有一个线程访问共享资源。信号量的主要功能是用于资源计数。

线程同步(临界区、互斥量、事件、信号量)相关推荐

  1. 1线程同步:互斥量,死锁

     1线程为什么要同步 A:共享资源,多个线程都可对共享资源操作. B:线程操作共享资源的先后顺序不确定. C:处理器对存储器的操作一般不是原子操作. 2互斥量 mutex操作原语 pthread_ ...

  2. 信号灯文件锁linux线程,linux——线程同步(互斥量、条件变量、信号灯、文件锁)...

    一.说明 linux的线程同步涉及: 1.互斥量 2.条件变量 3.信号灯 4.文件读写锁 信号灯很多时候被称为信号量,但个人仍觉得叫做信号灯比较好,因为可以与"SYSTEM V IPC的信 ...

  3. 进程间同步(互斥量、信号量)

    进程间同步可以使用互斥量mutex(互斥锁).信号量和文件锁. 进程间同步使用信号量: int sem_init(sem_t *sem, int pshared, unsigned int value ...

  4. java 信号量 互斥锁_线程同步(互斥锁与信号量的作用与区别)

    "信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里).而互斥锁是用在多线程多任务互斥的,一 ...

  5. RTT的线程同步篇——互斥量

    野火RTT第20章互斥量 2018年12月29日 10:47 互斥量不能在中断服务程序中使用. 互斥量是特殊的二值信号量,其"特殊"在哪呢?互斥量不同于二值信号量的地方在于:互斥量 ...

  6. 并发编程概念、程序线程进程、线程同步、互斥量、读写锁、协程并发

    多线程: 多线程就是同时执行多个应用程序,需要硬件的支持 同时执行:不是某个时间段同时,cpu切换的比较快,所有用户会感觉是在同时运行 并发与并行: 并行(parallel):指在同一时刻,有多条指令 ...

  7. 线程同步之互斥量(互斥锁)

    1 同步的概念 所谓同步, 即同时起步,协调一致.不同的对象, 对"同步" 的理解方式略有不同. 如,设备同步,是指在两个设备之间规定一个共同的时间参考: 数据库同步, 是指让两个 ...

  8. 线程同步之互斥量加锁解锁 死锁

    与互斥锁相关API       互斥量(mutex)从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁.对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻 ...

  9. Linux 多线程同步机制:互斥量、信号量、条件变量

    互斥量:互斥量提供对共享资源的保护访问,它的两种状态:lock和unlock,用来保证某段时间内只有一个线程使用共享资源,互斥量的数据类型是pthread_mutex_t 主要涉及函数:pthread ...

  10. linux线程同步(1)-互斥量

    一.概述                                                   互斥量是线程同步的一种机制,用来保护多线程的共享资源.同一时刻,只允许一个线程对临界区进行 ...

最新文章

  1. R使用neuralnet包构建神经网络回归模型并与线性回归模型对比实战
  2. 【正视CSS 06】构建我们自己的世界观!
  3. (8)nginx:反向代理和负载均衡
  4. iSCSI 2-环境搭建二
  5. centos7 mysql
  6. Lucene中string docvalues使用utf-16的优化
  7. Python_生成器Generator
  8. C++常量的引用 const
  9. Objcet_类的方法
  10. java日期减去天数得日期_Java日期添加天数,减去天数,日历
  11. Leetcode995 Minimum Number of K Consecutive Bit Flips解决方案
  12. watch 深度监听及立即执行
  13. A股市场,价投者眼中的10大金股,值得收藏(名单)
  14. 3、线性表的顺序存储结构(顺序表)
  15. 打印机无线连接台式机、笔记本找不到目标打印机解决方案
  16. 基于单片机住宅家庭防盗报警系统设计-毕设课设资料
  17. opc ua 用哪种语言编写_OPC UA是什么 OPC UA简介
  18. xdoj 238 数组鞍点 二维数组 循环
  19. 关于经典面试一年多少秒的思考!启发#define与UL!
  20. spring框架xml的几种配置方式

热门文章

  1. Dremel-大数据上的交互式分析
  2. 男士穿衣 - 博文预览
  3. Java 11 – ChaCha20-Poly1305加密示例
  4. OnePiece 之 Asp.Net 菜鸟也来做开发(二)
  5. Android layout_gravity 和 gravity的区别
  6. 每日词根——count
  7. C 语言常量pi,如何在C中使用PI常数
  8. 【C语言】do-while()循环
  9. 无需越狱,iPhone修改状态栏运营商名称
  10. MMORPG游戏设计