一文搞定c++多线程同步机制

C++的四种多线程同步机制 C++多线程同步机制摘要 C++提供了四种核心多线程同步机制来保证线程安全和资源共享: 互斥锁:通过独占访问保护共享资源,防止数据竞争。常用std::mutex配合RAII包装类(std::lock_guard/std::unique_lock)自动管理锁生命周期。 条件变量:实现线程间精准时序协调,通过等待/通知机制解决生产者-消费者问题。必须与互斥锁配合使用,注意处理虚假唤醒。 信号量:基于资源计数器控制并发访问数量,适合线程池等场景。C++20正式引入std::counting_semaphore。 阅读详情
AI摘要

本文详细介绍了C++多线程编程中的同步与互斥概念,解释了为什么需要线程同步,通过具体示例展示了mutex互斥锁、临界区、事件、信号量和互斥量等同步机制的使用方法。

c++多线程同步机制

前序文章:一文搞定c++多线程

同步与互斥

现代操作系统都是多任务操作系统,通常同一时刻有大量可执行实体,则运行着的大量任务可能需要访问或使用同一资源,或者说这些任务之间具有依赖性。

  • 线程同步:线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。例如:两个线程A和B在运行过程中协同步调,按预定的先后次序运行,比如 A 任务的运行依赖于 B 任务产生的数据。
  • 线程互斥:线程互斥是指对于共享的操作系统资源,在各线程访问时具有排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许有限的线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。例如:两个线程A和B在运行过程中共享同一变量,但为了保持变量的一致性,如果A占有了该资源则B需要等待A释放才行,如果B占有了该资源需要等待B释放才行。
为什么需要线程同步

​ 由于现在操作系统支持多个线程运行,可能多个线程之间会共享同一资源。当多个线程去访问同一资源时,如果不加以干预,可能会引起冲突。例如,多个线程同时访问同一个全局变量,如果都是读取操作,则不会出现问题。如果一个线程负责改变此变量的值,而其他线程负责同时读取变量内容,则不能保证读取到的数据是经过写线程修改后的。为了确保读线程读取到的是经过修改的变量,就必须在向变量写入数据时禁止其他线程对其的任何访问,直至赋值过程结束后再解除对其他线程的访问限制。这种保证线程能了解其他线程任务处理结束后的处理结果而采取的保护措施即为线程同步。

​ 举个例子,现在你银行卡里有100元,然后一个线程去执行消费,一个线程去执行充值,如果不加以干预,则可能出现这样的情况:消费的线程读取到你的卡里有100元,然后由于线程切换保存了当前的状态就去执行充值线程,充值线程完成充值后你的卡里实际上应该是10000元,然后切换到消费进程,消费进程由于已经读取过卡里的钱所以会直接进行之后的操作,完成后计算得到卡里的钱应该改为50,这便会将你真实的卡里的钱改成50,这当然是我们不希望看到的!如果进行了线程同步操作,当消费线程进行时,由于这是对数据进行写的操作,那么其他充值线程都需要被阻塞直至消费进程结束占据资源,这样便不会导致数据的不一致。

​ 举个代码例子,两个线程对一个共享数据进行++操作并且输出出来,代码如下:

#include <iostream>
#include <thread>
#include <Windows.h>
using namespace std;
int share = 0;  //共享变量
void thread1()
{
    while(share<20)
    {
        share++;
        cout << "this is thread1! share is " << share << endl;
        Sleep(100);
    }
}
void thread2()
{
    while (share < 20)
    {
        share++;
        cout << "this is thread2! share is " << share << endl;
        Sleep(100);
    }
}

int main()
{
    thread task1(thread1); 
    thread task2(thread2); 
    task1.join();
    task2.join();
    cout << "main thread!" << endl;
}   

某一次的运行结果:

PS D:\vscode_c> ./test
this is thread1! share is 2
this is thread2! share is 2
this is thread1! share is 3
this is thread2! share is 4
this is thread1! share is this is thread2! share is 6
6
this is thread2! share is 8
this is thread1! share is 8
this is thread2! share is 9
this is thread1! share is 10
this is thread1! share is 12
this is thread2! share is 13
this is thread1! share is 14
this is thread2! share is 15
this is thread1! share is 16
this is thread2! share is this is thread1! share is 18
18
this is thread1! share is 20
this is thread2! share is 20
main thread!

可以看到,不但出现两个线程读取的变量值一样的现象(我们当然期望的是每一行都是一个唯一的数字并且有一个换行),还出现了cout的内容包括数字和换行符位置也有些错乱。主要原因是share和cout的缓冲区是thread1和thread2共享的,由于两个线程同时运行,便可能将一个已经修改的值读取,或者将另一个线程已经读取但是未修改的值进行读取,还有可能将另一个线程已经放入缓冲区的内容输出。当然,这个输出是不符合我们预期的!

mutex互斥锁

互斥锁是一种简单的通过加锁的方式控制多个线程对共享资源的访问,互斥锁有两个状态,上锁与解锁(lock和unlock)。lock互斥锁是一个原子操作,这说明在同一时刻只能有一个线程锁住互斥锁,不会出现同时上锁的情况,同时,互斥锁具有唯一性,一旦上锁,其他线程不能够再将其锁住。当一个互斥锁被锁住时,其他希望锁住该锁的线程将被挂起,直至该互斥锁被unlock解开,则这些线程将被唤醒并其中一个将再次抢占成功。

互斥锁的通常执行流程如下:

  • 在访问共享资源的临界区前,将互斥锁锁住 lock
  • 在完成访问共享资源的操作后,将互斥锁unlock
  • 这期间,其他线程如果需要访问共享资源将调用lock,将自身挂起,直至该互斥锁被unlock才行

利用互斥锁修改上面的代码:

#include <iostream>
#include <thread>
#include <Windows.h>
#include<mutex>
using namespace std;
mutex mut;
int share = 0;
void thread1()
{
    while(share<20)
    {
        mut.lock();   //将互斥锁进行lock
        share++;
        cout << "this is thread1! share is " << share << endl;
        Sleep(100);
        mut.unlock();  //unlock 解开互斥锁
    }
}
void thread2()
{
    while (share < 20)
    {
        mut.lock();   //将互斥锁进行lock
        share++;
        cout << "this is thread2! share is " << share << endl;
        Sleep(100);
        mut.unlock();  //unlock 解开互斥锁
    }
}

int main()
{
    thread task1(thread1); 
    thread task2(thread2); 
    task1.join();
    task2.join();
    cout << "main thread!" << endl;
}   

查看运行结果:

PS D:\vscode_c> ./test                
this is thread1! share is 1
this is thread2! share is 2
this is thread1! share is 3
this is thread2! share is 4
this is thread2! share is 5
this is thread2! share is 6
this is thread2! share is 7
this is thread2! share is 8
this is thread2! share is 9
this is thread1! share is 10
this is thread2! share is 11
this is thread1! share is 12
this is thread1! share is 13
this is thread1! share is 14
this is thread2! share is 15
this is thread2! share is 16
this is thread2! share is 17
this is thread1! share is 18
this is thread1! share is 19
this is thread2! share is 20
main thread!

很显然,这是符合我们预期的。不过事情总要做到更好,这个方法有什么问题呢?试想一下,如果在lock和unlock之间发生了异常,则可能永远不会执行到unlock,另一个进程将永远被挂起在那里等待。

为了解决该问题,根据对象的析构函数自动调用的原理,c++11推出了std::lock_guard自动释放锁,其原理是:声明一个局部的lock_guard对象,在其构造函数中进行加锁,在其析构函数中进行解锁。最终的结果就是:在定义该局部对象的时候加锁(调用构造函数),出了该对象作用域的时候解锁(调用析构函数)。

在C++中,通过构造std::mutex的实例来创建互斥元,可通过调用其成员函数lock()和unlock()来实现加锁和解锁,然后这是不推荐的做法,因为这要求程序员在离开函数的每条代码路径上都调用unlock(),包括由于异常所导致的在内。作为替代,标准库提供了std::lock_guard类模板,实现了互斥元的RAII惯用语法(资源获取即初始化)。该对象在构造时锁定所给的互斥元,析构时解锁该互斥元,从而保证被锁定的互斥元始终被正确解锁

#include <iostream>
#include <thread>
#include <Windows.h>
#include<mutex>
using namespace std;
mutex mut;
int share = 0;
void thread1()
{
    while(share<20)
    {
        std::lock_guard<std::mutex> mtx_locker(mut);  //用lock_guard实现互斥锁
        if(share>=20)
            break;
        share++;
        cout << "this is thread1! share is " << share << endl;
        Sleep(100);
    }
}
void thread2()
{
    while (share < 20)
    {
            std::lock_guard<std::mutex> mtx_locker(mut);  //用lock_guard实现互斥锁
            if (share >= 20)
                break;
            share++;
            cout << "this is thread2! share is " << share << endl;
            Sleep(100);
    }
}

int main()
{
    thread task1(thread1); 
    thread task2(thread2); 
    task1.join();
    task2.join();
    cout << "main thread!" << endl;
}   

运行结果:

PS D:\vscode_c> ./test                
this is thread1! share is 1
this is thread2! share is 2
this is thread2! share is 3
this is thread2! share is 4
this is thread2! share is 5
this is thread2! share is 6
this is thread2! share is 7
this is thread2! share is 8
this is thread2! share is 9
this is thread2! share is 10
this is thread2! share is 11
this is thread1! share is 12
this is thread2! share is 13
this is thread2! share is 14
this is thread2! share is 15
this is thread2! share is 16
this is thread1! share is 17
this is thread2! share is 18
this is thread2! share is 19
this is thread1! share is 20
main thread!
win32的四种同步方式
临界区

临界区 (Critical Section) 是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。

临界区在使用时以CRITICAL_SECTION结构对象保护共享资源,并分别用 EnterCriticalSection()和LeaveCriticalSection() 函数去标识和释放一个临界区。所用到的CRITICAL_SECTION结构对象必须经过InitializeCriticalSection() 的初始化后才能使用,而且必须确保所有线程中的任何试图访问此共享资源的代码都处在此临界区的保护之下。否则临界区将不会起到应有的作用,共享资源依然有被破坏的可能。

临界区的使用:

  • 定义一个CRITICAL_SECTION类型的变量

  • 调用InitializeCriticalSection函数对变量进行初始化,函数的作用是初始化临界区,唯一的参数是指向结构体CRITICAL_SECTION的指针变量

    VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection )
    
  • 为了将某段代码设置为临界区,在进入这段代码前调用EnterCriticalSection函数。该函数的作用是使调用该函数的线程进入已经初始化的临界区,并拥有该临界区的所有权。这是一个阻塞函数,如果线程获得临界区的所有权成功,则该函数将返回,调用线程继续执行,否则该函数将一直等待,这样会造成该函数的调用线程也一直等待。如果不想让调用线程等待(非阻塞),则应该使用TryEnterCriticalSection函数

    VOID WINAPI EnterCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection);
    
  • 在临界区代码后,需要调用LeaveCriticalSection函数。该函数的作用是使调用该函数的线程离开临界区并释放对该临界区的所有权,以便让其他线程也获得访问该共享资源的机会

    void WINAPI LeaveCriticalSection( _Inout_LPCRITICAL_SECTION lpCriticalSection);
    

    如果一个线程在进入临界区后没有调用LeaveCriticalSection,则会出现等待进入临界区的线程无限期等待的问题

  • 最后释放掉CRITICAL_SECTION结构指针,该函数的作用是删除程序中已经被初始化的临界区。如果函数调用成功,则程序会将内存中的临界区删除,防止出现内存错误。

    void WINAPI DeleteCriticalSection(_Inout_ LPCRITICAL_SECTION lpCriticalSection);
    

tips:单进程的线程可以使用临界资源对象来解决同步互斥问题,该对象不能保证哪个线程能够获得到临界资源对象,因而该系统能公平的对待每一个线程

利用临界区解决上面的问题:

#include <iostream>
#include <thread>
#include <windows.h>
#include<mutex>
using namespace std;
CRITICAL_SECTION Critical; //定义临界区句柄
int share = 0;
void thread1()
{
    while(share<20)
    {
        EnterCriticalSection(&Critical);
        if(share>=20)
            break;
        share++;
        cout << "this is thread1! share is " << share << endl;
        Sleep(100);
        LeaveCriticalSection(&Critical);
    }
}
void thread2()
{
    while (share < 20)
    {
        EnterCriticalSection(&Critical);
        if (share >= 20)
            break;
        share++;
        cout << "this is thread2! share is " << share << endl;
        Sleep(100);
        LeaveCriticalSection(&Critical);
    }
}

int main()
{
    InitializeCriticalSection(&Critical); //初始化临界区对象
    thread task1(thread1); 
    thread task2(thread2); 
    task1.join();
    task2.join();
    cout << "main thread!" << endl;
}   

写起来和mutex类似,主要注意 的是一定要先初始化临界区对象。

运行结果:

PS D:\vscode_c> ./test                
this is thread1! share is 1
this is thread1! share is 2
this is thread1! share is 3
this is thread1! share is 4
this is thread1! share is 5
this is thread1! share is 6
this is thread2! share is 7
this is thread2! share is 8
this is thread2! share is 9
this is thread1! share is 10
this is thread1! share is 11
this is thread1! share is 12
this is thread1! share is 13
this is thread1! share is 14
this is thread1! share is 15
this is thread1! share is 16
this is thread1! share is 17
this is thread1! share is 18
this is thread1! share is 19
this is thread1! share is 20
main thread!
事件

事件(Event)是WIN32提供的最灵活的线程间同步方式,事件可以处于激发状态或未激发状态。应用时,通过使用 CreateEvent 函数创建事件,然后使用信号控制线程运行。其中将事件变为有信号可使用 SetEvent 函数,将事件信号复位(变为无信号)可使用 ResetEvent 函数,信号可以配合 WaitForSingleObject 函数对线程的同步进行控制,当有信号时,此函数便会放行;无信号时,此函数会将阻塞。

根据状态变迁方式的不同,事件可分为两类:
(1)手动设置:这种对象只可能用程序手动设置,在需要该事件或者事件发生时,采用SetEvent及ResetEvent来进行设置。
(2)自动恢复:一旦事件发生并被处理后,自动恢复到没有事件状态,不需要再次设置。

相关的函数:

函数名函数说明
CreateEventCreates or opens a named or unnamed event object.
CreateEventExCreates or opens a named or unnamed event object and returns a handle to the object.
OpenEventOpens an existing named event object.
PulseEventSets the specified event object to the signaled state and then resets it to the nonsignaled state after releasing the appropriate number of waiting threads.
ResetEventSets the specified event object to the nonsignaled state.
SetEventSets the specified event object to the signaled state.

CreateEvent用于创建事件对象,函数原型为:

HANDLE WINAPI CreateEvent(
  _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,
  _In_     BOOL                  bManualReset,
  _In_     BOOL                  bInitialState,  
  _In_opt_ LPCTSTR               lpName
);

着重强调一下第二个参数,CreateEvent的第二个参数 bManualReset 表示指定将事件对象创建成手动复原还是自动复原,如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当事件被一个等待线程释放以后,系统将会自动将事件状态复原为无信号状态。第三个参数bInitialState 表示事件对象的初始状态。如果为true,则表示该事件对象初始时为有信号状态

利用事件解决上面的问题:

#include <iostream>
#include <thread>
#include <windows.h>
#include<mutex>
using namespace std;
HANDLE hEvent; //定义事件句柄
int share = 0;
void thread1()
{
    while(share<20)
    {
        WaitForSingleObject(hEvent, INFINITE); //等待对象为有信号状态
        if(share>=20)
            break;
        share++;
        cout << "this is thread1! share is " << share << endl;
        Sleep(100);
        SetEvent(hEvent);  //将事件设置为有信号状态
    }
}
void thread2()
{
    while (share < 20)
    {
        WaitForSingleObject(hEvent, INFINITE); //等待对象为有信号状态
        if (share >= 20)
            break;
        share++;
        cout << "this is thread2! share is " << share << endl;
        Sleep(100);
        SetEvent(hEvent);  //将事件设置为有信号状态
    }
}

int main()
{
    hEvent = CreateEvent(NULL, FALSE, TRUE, "event");  //创建事件   是自动恢复状态
    thread task1(thread1); 
    thread task2(thread2); 
    task1.join();
    task2.join();
    cout << "main thread!" << endl;
}   

运行结果:

PS D:\vscode_c> ./test                
this is thread1! share is 1
this is thread2! share is 2
this is thread1! share is 3
this is thread2! share is 4
this is thread1! share is 5
this is thread2! share is 6
this is thread1! share is 7
this is thread2! share is 8
this is thread1! share is 9
this is thread2! share is 10
this is thread1! share is 11
this is thread2! share is 12
this is thread1! share is 13
this is thread2! share is 14
this is thread1! share is 15
this is thread2! share is 16
this is thread1! share is 17
this is thread2! share is 18
this is thread1! share is 19
this is thread2! share is 20
main thread!
信号量

信号量是维护0到指定最大值之间的同步对象,用于线程的同步或者限制线程运行的数量。信号量状态在其计数大于0时是有信号的,而其计数是0时是无信号的。信号量对象在控制上可以支持有限数量共享资源的访问。

通常来说,信号量具有如下特点:

  • 如果当前资源的数量大于0,则信号量有效
  • 如果当前资源数量是0,则信号量无效
  • 当前资源的数量不能够为负值
  • 当前资源数量一定小于等于最大资源数量

信号量相关的函数:

//头文件
#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                 //信号量对象的名称;
);

//等待信号量API
DWORD WINAPI WaitForSingleObject(
  _In_ HANDLE hHandle,          //信号量对象句柄
  _In_ DWORD  dwMilliseconds    //等待信号量时间,INFINET代表永久等待;
);

//打开信号量
HANDLE OpenSemaphore (
   DWORD fdwAccess,      //access
   BOOL bInherithandle,  //如果允许子进程继承句柄,则设为TRUE
   PCTSTR pszName  //指定要打开的对象的名字
  );

//释放信号量句柄
BOOL WINAPI ReleaseSemaphore(
  _In_      HANDLE hSemaphore,         //信号量对象句柄;
  _In_      LONG   lReleaseCount,      //信号量释放的值,必须大于0;
  _Out_opt_ LPLONG lpPreviousCount     //前一次信号量值的指针,不需要可置为空;
);

用信号量解决上面的问题:

#include <iostream>
#include <thread>
#include <windows.h>
#include<mutex>
using namespace std;
HANDLE hSemaphore; //定义信号量句柄
int share = 0;
void thread1()
{
    while(share<20)
    {
        WaitForSingleObject(hSemaphore, INFINITE); //等待信号量为有信号状态
        if(share>=20)
            break;
        share++;
        cout << "this is thread1! share is " << share << endl;
        Sleep(100);
        ReleaseSemaphore(hSemaphore, 1, nullptr);  //释放信号量
    }
}
void thread2()
{
    while (share < 20)
    {
        WaitForSingleObject(hSemaphore, INFINITE); //等待信号量为有信号状态
        if (share >= 20)
            break;
        share++;
        cout << "this is thread2! share is " << share << endl;
        Sleep(100);
        ReleaseSemaphore(hSemaphore, 1, nullptr); //释放信号量
    }
}

int main()
{
    hSemaphore = CreateSemaphore(NULL, 1, 20, "semaphore"); //创建信号量
    thread task1(thread1); 
    thread task2(thread2); 
    task1.join();
    task2.join();
    cout << "main thread!" << endl;
}   

运行结果:

PS D:\vscode_c> ./test                
this is thread1! share is 1
this is thread2! share is 2
this is thread1! share is 3
this is thread2! share is 4
this is thread1! share is 5
this is thread2! share is 6
this is thread1! share is 7
this is thread2! share is 8
this is thread1! share is 9
this is thread2! share is 10
this is thread1! share is 11
this is thread2! share is 12
this is thread1! share is 13
this is thread2! share is 14
this is thread1! share is 15
this is thread2! share is 16
this is thread1! share is 17
this is thread2! share is 18
this is thread1! share is 19
this is thread2! share is 20
main thread!
互斥量

windows下提供有互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。不过该互斥量和mutex基本一样,所以用移植性更好的mutex更好。

互斥量主要的函数:

//创建互斥量
HANDLE WINAPI CreateMutex(
  __in          LPSECURITY_ATTRIBUTES lpMutexAttributes,//互斥对象的安全属性
  __in          BOOL bInitialOwner,//互斥对象的初始状态;TRUE表示互斥对象的线程ID为当前调度线程的线程ID,当前创建互斥对象的线程具有他的拥有权,互斥对象的递归计数器为1
  __in          LPCTSTR lpName//互斥对象的名称,NULL表示创建一个匿名的互斥对象
);
//释放互斥量
BOOL WINAPI ReleaseMutex(
  __in          HANDLE hMutex
);
//等待互斥量
DWORD WINAPI WaitForSingleObject(
  __in          HANDLE hHandle,//等待内核对象句柄
  __in          DWORD dwMilliseconds//等待时间,INFINITE表示无限等待
);

用互斥量解决上面的问题:

#include <iostream>
#include <thread>
#include <windows.h>
#include<mutex>
using namespace std;
HANDLE hMutex; //定义互斥对象句柄
int share = 0;
void thread1()
{
    while(share<20)
    {
        WaitForSingleObject(hMutex, INFINITE);  //等待互斥量
        if(share>=20)
            break;
        share++;
        cout << "this is thread1! share is " << share << endl;
        Sleep(100);
        ReleaseMutex(hMutex);  //释放互斥量
    }
}
void thread2()
{
    while (share < 20)
    {
        WaitForSingleObject(hMutex, INFINITE); //等待互斥量
        if (share >= 20)
            break;
        share++;
        cout << "this is thread2! share is " << share << endl;
        Sleep(100);
        ReleaseMutex(hMutex); //释放互斥量
    }
}

int main()
{
    hMutex = CreateMutex(NULL, false, "mutex"); //创建互斥对象
    thread task1(thread1); 
    thread task2(thread2); 
    task1.join();
    task2.join();
    cout << "main thread!" << endl;
}   

运行结果:

PS D:\vscode_c> ./test                
this is thread1! share is 1
this is thread2! share is 2
this is thread1! share is 3
this is thread2! share is 4
this is thread1! share is 5
this is thread2! share is 6
this is thread1! share is 7
this is thread2! share is 8
this is thread1! share is 9
this is thread2! share is 10
this is thread1! share is 11
this is thread2! share is 12
this is thread1! share is 13
this is thread2! share is 14
this is thread1! share is 15
this is thread2! share is 16
this is thread1! share is 17
this is thread2! share is 18
this is thread1! share is 19
this is thread2! share is 20
main thread!
C++中的多线程与异常(疑惑) thread and exception 阅读详情

相关推荐

C++ 操作系统进程中线程同步的四种方式

线程同步的定义:       同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。“同”字从字面上容易理解为一起动作       其实不是,“同”字应是指协同、协助、互相配合。如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。        所谓同步,就是在发出一个功能调用时,

MISAYAONE的博客 2516

【C++】多线程同步

线程间的同步方法大体可以分为两类:用户模式和内核模式。内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。 用户模式下的方法有:原子操作(例如一个单一的全局变量)、临界区。特点是:同步速度特别快。 内核模式下的方法有:事件、信号量、互斥量。同步速度较慢,但适用性比较好。 **临界区:**通过对多线程的串行化来访问公共资源或一段代码、速度快,适合控制数据访问。 **互斥量:**为协调共同对一个共享资源的单独访问而设计的。 *

目标:MVP 7629

C++怎么学?零基础也可以,一份完整学习路线图助你搞定!

提供一条清晰、系统且实践导向的C++学习路径。从基础语法、面向对象、泛型编程、异常处理,逐步深入STL、智能指针、并发编程等现代C++高级特性。无论是新手还是希望提升,通过系统学习与项目实践,助您将理论知识转化为实际解决问题的能力。

Lion_Long的博客 4027

C++多线程同步

摘要:本文介绍了C++11中如何开启新线程,并详细讲解了线程的基础同步原语:mutex, lock_guard, unique_lock, condition variable和semaphore等。如何采用async, packaged_task和promise实现future同步机制?怎样处理spurious wakeup?本文以质数判定服务为例为大家分享C++多线程同步措施! 1. C++线程和基础同步原语 Thread mutex, lock_guard, unique_lock con.

书山有路勤为径 5529

OPTI Toolbox v2.28 安装与 3 个求解器补全:SCIP、SeDuMi、MOSEK 配置详解

本文详细介绍了OPTI Toolbox v2.28在MATLAB环境下的完整安装指南,包括SCIP、SeDuMi和MOSEK三个关键求解器的配置与优化技巧。通过分步指导和实战案例,帮助用户高效搭建强大的优化工具包,提升混合整数规划、二次约束二次规划等复杂问题的求解效率。

weixin_29039773的博客 350

Docker环境下onlyOffice 7.5.1破解版部署全攻略(含ARM架构适配)

本文提供了在Docker环境中部署OnlyOffice 7.5.1破解版的完整实战指南。内容涵盖x86与ARM架构服务器的详细部署步骤、环境配置、持久化存储设置以及生产环境下的安全与性能优化。重点解决了ARM架构适配的常见问题,并分享了使用Docker Compose进行服务编排和日常维护的技巧,帮助用户快速搭建私有化文档协作平台。

redis7keeper的博客 354

嵌入式C/C++进阶-进程/线程通信《共享内存,一文搞定》

共享内存是一种进程间通信(IPC)机制,允许多个进程直接访问同一块内存区域,从而实现高速数据交换。✅让不同进程“共享同一片内存”,从而跳过内核缓冲区,数据不需要多次拷贝。⚡速度最快的进程间通信方式(零拷贝)🚫无内核缓冲区(直接读写)🧩需要同步机制保证一致性📦 可配合信号量、互斥锁等实现安全通信。

csyys1的博客 1006

解决Windows 10安装语言包时遇到的0x800F0954错误

在安装Windows 10操作系统的语言包时,如果遇到错误代码0x800F0954的提示,这无疑会让许多用户感到困惑和无助。这个错误通常意味着在安装过程中遇到了一些阻碍,导致无法正常完成语言包的安装。本文将为大家详细解析这个错误代码0x800F0954,并提供一系列有效的解决方法,帮助大家顺利安装所需的语言包。

nntxthml的博客 3793

多线程并发控制难题,一文搞定量化交易系统的线程调度与资源竞争

掌握量化交易系统的多线程并发控制难题,提供高效线程调度与资源竞争解决方案。适用于高频交易、实时行情处理场景,采用锁机制优化与任务队列分离设计,提升系统稳定性与执行效率。实战经验总结,值得收藏。

DevPath的博客 349

探索热阻测试仪在半导体器件热管理中的应用与前景

T3Ster是MicReD研发的热测试仪,运用JEDEC稳态实时测试方法,专业测试分离或集成的双极型晶体管、MOS晶体管、常见的三极管、LED封装和半导体闸流管等器件的热特性。利用瞬态温升技术,可测得器件稳态热阻和温升,不但可以测得半导体器件稳态热阻和温升,而且可以直接测量各部分对于温升的贡献,计算芯片热流路径上的纵向热阻构成,对器件热可靠性设计、散热问题解决、产品性能提升和长期可靠性至关重要。关键词:T3ster,Micred,功率循环,结温测试,热阻测试,结温热阻测试,半导体热特性测试;

anscos_Olivia的博客 896

PLC编程标准之争:IEC61131与IEC61499核心区别与选型指南

在工业自动化领域,PLC编程长期依赖IEC61131标准,它定义了梯形图、结构化文本等语言,通过周期扫描机制保证确定性执行。然而,随着工业4.0和分布式控制场景的兴起,IEC61499以事件驱动模型打破传统范式,让功能块能跨设备协同,实现更灵活的柔性产线和模块化系统。理解两者在程序结构、数据交互、部署方式和实时性上的差异,是选型与工程落地的关键。本文从基础概念出发,剖析执行模型与架构演进,结合一线调试经验,帮助工程师在传统PLC与下一代标准之间做出理性决策。

weixin_27014595的博客 270

要复习的那些点

2021年离职计划:30岁之前,还是期望在大厂工作,期望薪资是50万/年、涨幅在30%,工作地点在杭州 时间 动作 2021:02:01 离职原因(利弊权衡): 目前现状: 部门leader 8月份被降级、小组目前业务大部分被拿走 、对目前小组的发展前景不是很看好。 离职好处: 1.工资可以又较大涨幅、寻找有价值业务 2.杭州的话,离黄瀛更近。目前尚不清楚黄瀛是否期望一直在杭州、还是说等关系稳定后可以一起去其他城市打拼。 活水选择:

dainandainan1的博客 1635

Lumerical官方案例、FDTD时域有限差分法仿真学习(三)——环形谐振器(Ring resonator)之第二&第三部分

二、使用 MODE 的参数提取和蒙特卡罗 三、最终参数提取

weixin_45719141的博客 1万+

C++之线程同步

线程同步总结

u010378559的博客 1405

5、数字图像采集:X射线成像技术全解析

本文全面解析了数字图像采集中的X射线成像技术,涵盖数字X射线探测器、荧光透视与血管造影、乳腺摄影以及计算机断层扫描(CT)的图像重建原理。详细介绍了TFT平板探测器的优势、DSA技术在血管成像中的应用、乳腺摄影对高对比度和分辨率的需求,以及CT中基于Radon变换和滤波反投影的重建方法。同时探讨了噪声、部分容积效应、金属伪影和运动伪影等常见问题及其影响。最后总结了各类X射线成像技术的特点与应用场景,并展望了未来在图像质量提升和剂量优化方面的技术发展方向。

l6m7n8的博客 128

零基础教你学RDMA驱动开发--第8篇:队列对(QP)、完成队列(CQ)与工作请求(WR)机制剖析

本文深入剖析了RDMA(远程直接内存访问)中的核心机制:队列对(QP)、完成队列(CQ)和工作请求(WR)。QP作为通信端点包含发送队列(SQ)和接收队列(RQ);WR是用户提交的操作指令,转换为工作队列元素(WQE)后由网卡异步执行;CQ则提供完成通知机制。文章详细介绍了三者的协作原理,包括QP状态机、WR提交方式、CQ轮询机制,以及关键的信号控制技术(Signaled Mode)来优化性能。同时对比了SEND/RECV和RDMA WRITE两种模式的差异,提供了调试技巧和最佳实践。这些机制共同构成了RD

amy_mhd的博客 157

C++多线程编程——线程同步(保姆级-1.4W字)

线程同步是一种编程技术,它用于在多线程环境中确保多个线程能够正确、安全地共享和访问共享资源。线程同步的主要目的是防止数据竞争和不一致性,以及避免多个线程同时对同一数据进行修改或访问导致的问题。

qq_25218501的博客 2106

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

C++实现线程同步的几种方式

在C++中,通过构造std::mutex的实例来创建互斥元,可通过调用其成员函数lock()和unlock()来实现加锁和解锁,然后这是不推荐的做法,因为这要求程序员在离开函数的每条代码路径上都调用unlock(),包括由于异常所导致的在内。3.LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection),该函数的作用是使调用该函数的线程离开临界区并释放对该临界区的所有权,以便让其他线程也获得访问该共享资源的机会。该函数将在用户指定的事件对象上等待。

m0_74282605的博客 1573

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

C++多线程同步总结

C++ 多线程同步

yishuihanq的博客 1066

C++ 线程 一些同步方式

TOCTOC。

gopher9511的博客 1318

[C++]线程同步的几种方式

进程有自己的独立地址空间,因此进程之间重点关注通信,通信方式包括:管道Pipe、命名管道FIFO、消息队列MessageQueue、共享存储SharedMemory、信号量Semaphore、套接字Socket和信号Signal。线程除了线程栈外其他数据都是共享的,如果同时读写数据可能造成数据不一致甚至程序崩溃的后果,因此线程之间重点关注同步。互斥锁(又名互斥量)强调的是资源之间的访问互斥:每个线程在对共享资源操作前都会尝试先加锁,加锁成功才能操作,操作结束之后解锁。

FL1768317420的博客 3671

C++线程同步的方法

线程同步 线程之间通信的两个基本问题是互斥和同步。 线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。 线程互斥是指对于共享的操作系统资源在各线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。 线程互斥是一种特...

钟离默的个人博客 2315

C++多线程同步的5种方式:互斥锁mutex、条件变量condition_variable、信号量sempahore、异步操作future、原子操作atomic

C++多线程同步的5种方式讲解:互斥锁mutex、条件变量condition_variable、信号量sempahore、异步操作future、原子操作atomic。

欧特GO 2260

C++ 线程同步

一. 实现线程同步的方式 1. 临界区 临界区(Critical Section)是一段独占对某些共享资源访问的代码,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。 2. 事件(CEve...

SJLin 972

C++ 多线程同步

在C++中,多线程同步是非常重要的,因为多个线程同时访问共享资源可能会导致数据竞争和不一致性

weixin_39926429的博客 1966

C++ 并发编程指南(4)线程同步

文章目录一、线程间共享数据1、线程间共享数据的问题2、使用互斥元保护共享数据 一、线程间共享数据 1、线程间共享数据的问题 2、使用互斥元保护共享数据

个人技术博客 934
上一篇: 一文搞定c++多线程
下一篇: 一文实现玩转Git(新人必看)
coding丁
博客等级 码龄9年 383粉丝 147原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值