C++ 线程的学习---线程同步
因为是学习篇,写下是为了个人的学习与理解。故参考其他文章为多。
- 为什么需要线程同步?
在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理结果的了解应当在其处理任务完成后进行。
如果不采取适当的措施,其他线程往往会在线程处理任务结束前就去访问处理结果,这就很有可能得到有关处理结果的错误了解。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。
为了确保读线程读取到的是经过修改的变量,就必须在向变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。这种保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施即为线程同步。
- 线程之间通信的两个基本问题是互斥和同步
线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。
线程互斥是指对于共享的操作系统资源(指的是广义的”资源”,而不是Windows的.res文件,譬如全局变量就是一种共享资源),在各线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源
- 线程互斥是一种特殊的线程同步。
实际上,互斥和同步对应着线程间通信发生的两种情况:
当有多个线程访问共享资源而不使资源被破坏时;
当一个线程需要将某个任务已经完成的情况通知另外一个或多个线程时。
- 从大的方面讲,线程的同步可分用户模式的线程同步和内核对象的线程同步两大类
用户模式中线程的同步方法主要有原子访问和临界区等方法。其特点是同步速度特别快,适合于对线程运行速度有严格要求的场合。
内核对象的线程同步则主要由事件、等待定时器、信号量以及信号灯等内核对象构成。由于这种同步机制使用了内核对象,使用时必须将线程从用户模式切换到内核模式,而这种转换一般要耗费近千个CPU周期,因此同步速度较慢,但在适用性上却要远优于用户模式的线程同步方式。
- 在WIN32中,同步机制主要有:事件(Event); 信号量(semaphore); 互斥量(mutex); 临界区(Critical section)
文档参考: C++线程同步的四种方式(Windows)
代码示例:
Project 1:
#include <iostream>
#include <Windows.h>
#include <time.h>
using namespace std; struct RandomSum {
DWORD a;
DWORD b;
}; int main()
{
//srand((unsigned int)time(NULL));
cout << "inside process 1" << endl; HANDLE hEvent1 = CreateEvent(NULL, FALSE, TRUE, "MyEvent1"); //TRUE=>初始状态是有信号的
HANDLE hEvent2 = CreateEvent(NULL, FALSE, FALSE, "MyEvent2"); //FALSE=>初始状态是没有信号的 HANDLE create_file_mapping_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
0, 1024 * 1024, "data");
if (create_file_mapping_handle == NULL) {
printf("Cannot create file mapping. Error code: %d", GetLastError());
return 0;
} unsigned char* pData = (unsigned char*)MapViewOfFile(create_file_mapping_handle,
FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pData == NULL) {
printf("Cannot get pointer to file mapping. Error code: %d", GetLastError());
CloseHandle(create_file_mapping_handle);
return 0;
} //create new process
PROCESS_INFORMATION process2;
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
if (!CreateProcess("D:\\Project 2.exe", NULL,
NULL, NULL, FALSE, 0, NULL, NULL, &si, &process2)) {
printf("Cannot create process.\n");
return 0;
}
CloseHandle(process2.hThread);
CloseHandle(process2.hProcess);
int counter = 0;
while (counter < 100) {
DWORD dwWaitResult = WaitForSingleObject(
hEvent1, // handle to mutex
INFINITE); // no time-out interval if (dwWaitResult == WAIT_OBJECT_0) {
cout << "am intrat in sender process si scriu in file mapping" << endl;
RandomSum test; DWORD randomNumber = rand() % 50;
test.a = randomNumber;
test.b = 2 * test.a;
memcpy(pData, &test, sizeof(RandomSum));
cout << "in process 1: " << "a = " << test.a << " b= " << test.b << endl;
cout << "last cout" << endl;
SetEvent(hEvent2);
} ++counter;
}
UnmapViewOfFile(pData);
CloseHandle(create_file_mapping_handle);
CloseHandle(hEvent1);
CloseHandle(hEvent2);
//getchar();
return 0;
}
Project 2:
#include <iostream>
#include <Windows.h>
#include <time.h>
using namespace std; struct RandomSum {
DWORD a;
DWORD b;
}; int main()
{
cout << "inside process2" << endl;
//srand((unsigned int)time(NULL)); cout << "something new in process2" << endl;
HANDLE hEvent1 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, "MyEvent1");
HANDLE hEvent2 = OpenEvent(EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, "MyEvent2"); LPCWSTR data = L"data";
HANDLE hData = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "data"); unsigned char* pData = (unsigned char*)MapViewOfFile(hData, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pData == NULL) {
printf("Cannot get pointer to file mapping. Error code: %d", GetLastError());
CloseHandle(hData);
return 0;
} int counter = 0;
while (counter < 100) {
DWORD dwWaitResult = WaitForSingleObject(
hEvent2, // handle to mutex
INFINITE); // no time-out interval
if (dwWaitResult == WAIT_OBJECT_0) {
cout << "a inceput procesul 2" << endl;
RandomSum test; memcpy(&test, pData, sizeof(RandomSum)); cout << "a is " << test.a << " and b is " << test.b << endl;
SetEvent(hEvent1);
}
++counter; }
UnmapViewOfFile(pData);
CloseHandle(hData);
CloseHandle(hEvent1);
CloseHandle(hEvent2);
//getchar();
}
更多样本请参阅: https://stackoverflow.com/a/65084216/11128312
C++ 线程的学习---线程同步的更多相关文章
- java线程API学习 线程池ThreadPoolExecutor(转)
线程池ThreadPoolExecutor继承自ExecutorService.是jdk1.5加入的新特性,将提交执行的任务在内部线程池中的可用线程中执行. 构造函数 ThreadPoolExecut ...
- Windows API学习---线程与内核对象的同步
前言 若干种内核对象,包括进程,线程和作业.可以将所有这些内核对象用于同步目的.对于线程同步来说,这些内核对象中的每种对象都可以说是处于已通知或未通知的状态之中.这种状态的切换是由Microsoft为 ...
- Java八个并发学习——线程同步工具CyclicBarrier
本文是一篇文章对网络的研究摘要,感谢您的无私分享. CyclicBarrier 类有一个整数初始值,此值表示将在同一点同步的线程数量.当当中一个线程到达确定点,它会调用await() 方法来等待其它线 ...
- Java学习之多线程(线程安全问题及线程同步)
一.线程安全问题产生前提:1.多线程操作共享数据2.线程任务中有多条代码 class Ticket implements Runnable { //2.共享数据 private int num = 1 ...
- Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统
理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...
- 关于协程的学习 & 线程栈默认10M
先看的这篇文章:http://blog.csdn.net/qq910894904/article/details/41699541 以nginx为代表的事件驱动的异步server正在横扫天下,那么事件 ...
- 菜鸟之旅——学习线程(Task)
前面两篇回顾线程和线程池的使用方法,微软在.NET4.5推出了新的线程模型-Task.本篇将简单的介绍Task的使用方法. Task与线程 Task与线程或者说线程池关系紧密,可以说是基于线程池实现的 ...
- Java线程机制学习
前面的文章中总结过Java中用来解决共享资源竞争导致线程不安全的几种常用方式: synchronized: ReentrantLock: ThreadLocal: 这些都是在简单介绍了基本用法的基础上 ...
- Python学习---线程/协程/进程学习 1220【all】
Python学习---线程基础学习 Python学习---线程锁/信号量/条件变量同步1221 Python学习---同步条件event/队列queue1223 Python学习---进程 1225 ...
- Java并发读书笔记:线程安全与互斥同步
目录 导致线程不安全的原因 什么是线程安全 不可变 绝对线程安全 相对线程安全 线程兼容 线程对立 互斥同步实现线程安全 synchronized内置锁 锁即对象 是否要释放锁 实现原理 啥是重进入? ...
随机推荐
- [转帖]比较不同CPU下的分支预测
https://plantegg.github.io/2023/04/16/%E6%AF%94%E8%BE%83%E4%B8%8D%E5%90%8CCPU%E4%B8%8B%E7%9A%84%E5%8 ...
- Oracle AWR学习之二-利用ChatGPT编写一键获取AWR报告的脚本
Oracle AWR学习之二-ChatGPT提升效率之n 背景 之前生成awr报告比较麻烦, 想着能够一键生成. 再辅以部分shell或者是python处理就可以进行细致的分析. 这一块其实还是比较简 ...
- [转帖]Kibana查询语言(KQL)
时间 2020-12-27 标签 html java 数据库 ide ui 翻译 日志 htm 对象 blog 栏目 HTML 繁體版 原文 https://www.cnblogs.com/-b ...
- 超级好用的elementui动态循环菜单
<template> <div> <el-menu @select="selectMenu" :default-active="curren ...
- k8s笔记——NodePort暴露nginx-controller实现https自动跳转自定义nodePort端口
安装nginx-controller并暴露nodePort helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx ...
- HEVC扩展备用安装方法
这个玩意微软商店免费但是下架了,购买需要RMB 安装 转到 https://store.rg-adguard.net/ 在左侧的下拉菜单选择"ProductId" 把链接中&quo ...
- 开源IM项目OpenIM单聊及万人群压测报告
单聊压测结论: 华为云主机s3一台:8核16G内存,网络带宽10Mb,普通磁盘(非SSD) 同时在线及压测客户端数量:1万 每秒钟发送消息量:2300条: 从发送到对方接收平均消息延时:5秒 群聊压测 ...
- Leetcode 2题 两数相加
题目链接 https://leetcode-cn.com/problems/add-two-numbers/ 题目描述 给你两个非空的链表,表示两个非负的整数.它们每位数字都是按照逆序的方式存储的,并 ...
- Go语言的原子操作atomic
atomic 原子操作 Go中原子操作的支持 CompareAndSwap(CAS) Swap(交换) Add(增加或减少) Load(原子读取) Store(原子写入) 原子操作与互斥锁的区别 at ...
- 快递单中抽取关键信息【一】----基于BiGRU+CR+预训练的词向量优化
相关文章: 1.快递单中抽取关键信息[一]----基于BiGRU+CR+预训练的词向量优化 2.快递单信息抽取[二]基于ERNIE1.0至ErnieGram + CRF预训练模型 3.快递单信息抽取[ ...