因为是学习篇,写下是为了个人的学习与理解。故参考其他文章为多。

  • 为什么需要线程同步?

在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作。更多的情况是一些线程进行某些处理操作,而其他的线程必须对其处理结果进行了解。正常情况下对这种处理结果的了解应当在其处理任务完成后进行。
     如果不采取适当的措施,其他线程往往会在线程处理任务结束前就去访问处理结果,这就很有可能得到有关处理结果的错误了解。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。
     为了确保读线程读取到的是经过修改的变量,就必须在向变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。这种保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施即为线程同步。

  • 线程之间通信的两个基本问题是互斥和同步

           线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。

           线程互斥是指对于共享的操作系统资源(指的是广义的”资源”,而不是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++ 线程的学习---线程同步的更多相关文章

  1. java线程API学习 线程池ThreadPoolExecutor(转)

    线程池ThreadPoolExecutor继承自ExecutorService.是jdk1.5加入的新特性,将提交执行的任务在内部线程池中的可用线程中执行. 构造函数 ThreadPoolExecut ...

  2. Windows API学习---线程与内核对象的同步

    前言 若干种内核对象,包括进程,线程和作业.可以将所有这些内核对象用于同步目的.对于线程同步来说,这些内核对象中的每种对象都可以说是处于已通知或未通知的状态之中.这种状态的切换是由Microsoft为 ...

  3. Java八个并发学习——线程同步工具CyclicBarrier

    本文是一篇文章对网络的研究摘要,感谢您的无私分享. CyclicBarrier 类有一个整数初始值,此值表示将在同一点同步的线程数量.当当中一个线程到达确定点,它会调用await() 方法来等待其它线 ...

  4. Java学习之多线程(线程安全问题及线程同步)

    一.线程安全问题产生前提:1.多线程操作共享数据2.线程任务中有多条代码 class Ticket implements Runnable { //2.共享数据 private int num = 1 ...

  5. Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统

    理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...

  6. 关于协程的学习 & 线程栈默认10M

    先看的这篇文章:http://blog.csdn.net/qq910894904/article/details/41699541 以nginx为代表的事件驱动的异步server正在横扫天下,那么事件 ...

  7. 菜鸟之旅——学习线程(Task)

    前面两篇回顾线程和线程池的使用方法,微软在.NET4.5推出了新的线程模型-Task.本篇将简单的介绍Task的使用方法. Task与线程 Task与线程或者说线程池关系紧密,可以说是基于线程池实现的 ...

  8. Java线程机制学习

    前面的文章中总结过Java中用来解决共享资源竞争导致线程不安全的几种常用方式: synchronized: ReentrantLock: ThreadLocal: 这些都是在简单介绍了基本用法的基础上 ...

  9. Python学习---线程/协程/进程学习 1220【all】

    Python学习---线程基础学习 Python学习---线程锁/信号量/条件变量同步1221 Python学习---同步条件event/队列queue1223 Python学习---进程 1225 ...

  10. Java并发读书笔记:线程安全与互斥同步

    目录 导致线程不安全的原因 什么是线程安全 不可变 绝对线程安全 相对线程安全 线程兼容 线程对立 互斥同步实现线程安全 synchronized内置锁 锁即对象 是否要释放锁 实现原理 啥是重进入? ...

随机推荐

  1. [转帖]SPEC CPU 2017 单线程整数性能测试与总结 (2022)

    https://zhuanlan.zhihu.com/p/574105237 x86处理器的整数性能在过去4年间取得了长足的进步 x86处理器移动端性能缩水非常严重 ARM公版的旗舰级处理器相比前代进 ...

  2. [转帖]Navicat连接openGauss数据库报错

    news/2023/10/19 21:23:19 错误信息:fe_sendauth:invalid authentication request from server:AUTH_REQ_SASL_C ...

  3. [转帖]Jmeter插件之ServerAgent服务器性能监控工具的安装和使用

    https://www.cnblogs.com/pachongshangdexuebi/p/13354201.html 一.前言    性能测试时我们关注的重要指标是:并发用户数,TPS,请求成功率, ...

  4. [转帖]Xargs用法详解

    https://www.cnblogs.com/cheyunhua/p/8796433.html 1. 简介 之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要, ...

  5. vue关于通过下标更改数组的理解

    案例1:通过下标更改数组失败 <template> <div> <el-button @click="handlerMe2"> 改变 arr & ...

  6. (数据科学学习手札122)Python+Dash快速web应用开发——内网穿透篇

    由我开源的先进Dash组件库feffery-antd-components正处于早期测试版本阶段,欢迎前往官网http://fac.feffery.tech/了解更多 1 简介 这是我的系列教程Pyt ...

  7. 使用telnet来调试游戏

    telnet是什么 Telnet协议是TCP/IP协议族中的一员,是Internet远程登陆服务的标准协议和主要方式.它为用户提供了在本地计算机上完成远程主机工作的能力 但是,telnet因为采用明文 ...

  8. 缩小ios的包体

    不选全部兼容设备 在xcode中导出ipa时,不勾选导出全部兼容性设备,这样导出的ipa包含两种架构:armv7和64 打包压缩 unity提供三种压缩模式可以选择,默认选择的是:default不压缩 ...

  9. vim 从嫌弃到依赖(3)——vim 普通模式

    在上一篇中,我们提到vim的几种模式,并且给出了一些基本的操作命令,包括移动光标,删除.替换操作.并且给出了几个重要的公式,理解这个公式对于理解vim和提高使用vim的效率来说至关重要.所以在这篇文章 ...

  10. MongoDB 选型介绍

    什么是 MongoDB 前言 MongoDB 的主要特性 MongoDB 对比关系型数据库 MySQL 什么时候考虑 MongoDB 参考 什么是 MongoDB 前言 MongoDB 是一个开源.高 ...