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

一文搞定c++多线程同步机制c 多线程同步机制同步与互斥现代操作系统都是多任务操作系统 通常同一时刻有大量可执行实体 则运行着的大量任务可能需要访问或使用同一资源 或者说这些任务之间具有依赖性 线程同步 线程同步是指线程之间所具有的一种制约关系 一个线程的执行依赖另一个线程的消息 当它没有得到另一个线程的消息时应等待 直到消息到达时才被唤醒 例如 两个线程 A 和 B 在运行过程中协同步调 按预定的先后次序运行 比如 A 任务的运行依赖于 B 任务产生的数据 线程互斥 线程互斥是指对于共享的操作系统资源 在各线程访问时具有排它性

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 函数对线程的同步进行控制,当有信号时,此函数便会放行;无信号时,此函数会将阻塞。

相关的函数:

函数名 函数说明
CreateEvent Creates or opens a named or unnamed event object.
CreateEventEx Creates or opens a named or unnamed event object and returns a handle to the object.
OpenEvent Opens an existing named event object.
PulseEvent Sets the specified event object to the signaled state and then resets it to the nonsignaled state after releasing the appropriate number of waiting threads.
ResetEvent Sets the specified event object to the nonsignaled state.
SetEvent Sets 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! 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/232121.html原文链接:https://javaforall.net

(0)
上一篇 2025年10月31日 下午3:01
下一篇 2025年10月31日 下午3:22


相关推荐

  • Python学习(十一)Python标识符命名规范

    Python学习(十一)Python标识符命名规范简单地理解,标识符就是一个名字,就好像我们每个人都有属于自己的名字,它的主要作用就是作为变量、函数、类、模块以及其他对象的名称。Python中标识符的命名不是随意的,而是要遵守一定的命令规则,比如说:1.标识符是由字符(A~Z和a~z)、下划线和数字组成,但第一个字符不能是数字。2.标识符不能和Python中的保留字相同。有关保留字,后续章节会详细介绍。3.Python中的标…

    2025年10月15日
    4
  • Ubuntu安装gcc-7.3.0[通俗易懂]

    Ubuntu安装gcc-7.3.0[通俗易懂]下载gcc-7.3.0.tar.gzfromhttps://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-7.3.0/gcc-7.3.0.tar.gz.有一些依赖提前安装:sudoaptinstalllibgmp-devsudoaptinstalllibmpfr-devsudoaptinstalllibmpc-devsudoaptinstalllibisl-devsudoaptinstallzlib1g-dev(建.

    2022年7月24日
    27
  • shmget物理内存_shmget共享内存

    shmget物理内存_shmget共享内存Linux 为共享内存提供了四种操作 1 共享内存对象的创建或获得 与其它两种 IPC 机制一样 进程在使用共享内存区域以前 必须通过系统调用 sys ipc call 值为 SHMGET 创建一个键值为 key 的共享内存对象 或获得已经存在的键值为 key 的某共享内存对象的引用标识符 以后对共享内存对象的访问都通过该引用标识符进行 对共享内存对象的创建或获得由函数 sys shmget 完成 其定义如下 int

    2026年3月19日
    2
  • python中yield的用法详解——最简单,最清晰的解释

    python中yield的用法详解——最简单,最清晰的解释首先我要吐槽一下,看程序的过程中遇见了yield这个关键字,然后百度的时候,发现没有一个能简单的让我懂的,讲起来真TM的都是头头是道,什么参数,什么传递的,还口口声声说自己的教程是最简单的,最浅显易懂的,我就想问没有有考虑过读者的感受。接下来是正题:首先,如果你还没有对yield有个初步分认识,那么你先把yield看做“return”,这个是直观的,它首先是个return,普通的retur…

    2022年7月23日
    14
  • 不知道部署哪个版本的Qwen3?一文看懂Qwen3本地部署的配置要求

    不知道部署哪个版本的Qwen3?一文看懂Qwen3本地部署的配置要求

    2026年3月13日
    1
  • jmeter测试并发200_jmeter并发测试实例

    jmeter测试并发200_jmeter并发测试实例相对并发和绝对并发相对并发:指在一个时间段内发生的事情 绝对并发:指在同一时刻发生的事情一:相对并发在jmeter的测试计划中添加线程组,设置线程属性,2秒之内启动2000个线程,其对应的相对并发为1000(线程数/启动时间)二:绝对并发一般使用同步定时器实现绝对并发,即当所有请求集合完毕之后一块出发1、jmeter线程组里面可以简单设置多线程,但是当你设置1秒钟50个线程时去看结果其实50个请求跑完并不是在1秒钟之内2、由于测试的机器本身性…

    2026年4月15日
    8

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号