C++多线程和并发


文章目录

  • C++多线程和并发
  • 前言
  • 一、进程、线程基本定义和函数使用。
    • 1.1 了解main函数、主线程、子线程
    • 1.2 多线程(join()、detach()、 joinable())
      • 1.2.1 join():
      • 1.2.2 detach():
      • 1.2.3 joinable():
    • 1.3 创建线程的手法
      • 1.3.1 调用函数
      • 1.3.2 调用结构体或类
      • 1.3.3 调用lamda表达式
    • 1.4 线程函数参数
      • 1.4.1 传递临时对象作为线程参数
      • 1.4.2 临时对象作为线程参数
      • 1.4.3 传递类对象、智能指针作为线程参数
      • 1.4.4 代码案例
    • 二、共享数据基础理解
      • 2.1 创建多个线程
      • 2.2 共享数据问题分析
      • 2.3 共享数据的保护案例代码
    • 三、 互斥量的用法
      • 3.1互斥量的基本概念
      • 3.2互斥量的用法
        • 3.1.1 lock(),unlock()
        • 3.1.2 std::lock_guard类模板
      • 3.3代码案例

前言

学习参考链接源码:
https://github.com/xiaopang59/multithreading
C++多线程和并发-更新
进程与线程的概念;
join、detach的使用;
创建线程的方法;
-2022/11/27
创建多个线程的方法进行管理、共享数据的概念 -2022/12/11


一、进程、线程基本定义和函数使用。

程序运行起来,生成一个进程,该进程所属的主线程开始自动运行;
实际上这个是主线程在运行,主线程从main()函数返回,则整个进程执行完毕。

1.1 了解main函数、主线程、子线程

  • 主线程从main()开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,就代表着我们这个线程运行结束。
  • 整个进程是否执行完毕的标志是 主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了。
  • 此时,一般情况下,如果其他子线程还没有执行完毕,那么这些子线程也会被操作系统强行终止。
  • 所以,一般情况下,我们得到一个结论:如果大家想保持子线程(自己用代码创建的线程)的运行状态的话,那么大家要让主线程一直保持运行,不要让主线程运行完毕;

1.2 多线程(join()、detach()、 joinable())

有两个线程在跑,相当整个程序的执行有两条线在同时走,所以,可以同时干两个事,即使一条线被堵住了,另一条线还是可以通行的。这就是多线程

join、detach用法如图:

1.2.1 join():

加入/汇合,说白了就是阻塞,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,然后主线程再往下走。
如果主线程执行完毕了,但子线程没执行完毕,这种程序员是不合格的,写出来的程序也是不稳定的;
所以如果不想让程序异常,应该是主线程 等待子线程执行完毕后,自己才能最终退出

1.2.2 detach():

传统多线程程序主线程要等待子线程执行完毕,然后自己再最后退出,也就是上面的join方式;

(1)detach:分离
也就是主线程不和子线程汇合了,你主线程执行你的,我子线程执行我的,你主线程也不必等我子线程运行结束,你可以先执行结束,这并不影响我子线程的执行。
(2)为什么引入detach():
我们创建了很多子线程,让主线程逐个等待子线程结束,这种变成方法不太好,所以引入了detach()。
(3)影响

  • 一旦detach()之后,与这个主线程关联的thread对象就会失去与这个主线程的关联,此时这个子线程就会驻留在后台运行(主线程跟子线程失去联系);
  • 这个子线程就相当于被C++运行时库接管,当这个子线程执行完毕后,由运行时库负责清理该线程相关的资源(守护线程);
  • detach()使我们对自己创建的子线程失去控制。

1.2.3 joinable():

判断是否可以成功使用join()或者detach()的;返回true(可以join或者detach)或者false(不能join或者detach)

1.3 创建线程的手法

thread:是个标准库里的类

1.3.1 调用函数

void myprint()
{cout << "我的线程开始执行了" << endl;
}
//myprint可调用对象。
thread mytobj(myprint);

1.3.2 调用结构体或类

  • 一旦调用了detach(),那我主线程执行结束了,我这里用的ta这个对象还在吗?(对象不在了)
  • 这个对象实际上是被 复制 到线程中去;执行完主线程后,ta会被销毁,但是所复制的TA对象依旧存在。
  • 所以,只要你这个TA类对象里没有引用,没有指针,那么就不会产生问题;
class TA
{public:int m_i;TA(int &i) : m_i(i){cout << "TA()构造函数被执行" << endl;}TA(const TA &ta) :m_i(ta.m_i){cout << "TA()拷贝构造函数被执行" << endl;}~TA(){cout << "TA()析构函数被执行" << endl;}void operator() ()// 不带参数{cout << "m_i1的值为:" << m_i << endl;// 产生不可预料的结果。cout << "m_i2的值为:" << m_i << endl;cout << "m_i3的值为:" << m_i << endl;cout << "m_i4的值为:" << m_i << endl;cout << "m_i5的值为:" << m_i << endl;cout << "m_i6的值为:" << m_i << endl;}
};
int main()
{int myi = 6;TA ta(myi);thread mytobj3(ta);//ta:可调用对象//mytobj3.join();// 等待子线程执行结束mytobj3.detach();cout << "hello world" << endl;return 0;
}

1.3.3 调用lamda表达式

int main()
{//用lanbda表达式auto mylamthread = [] {cout << "我的线程3开始执行了" << endl;//....cout << "我的线程3执行结束了" << endl;};thread mytobj4(mylamthread);//mytobj4.join();mytobj4.detach();return 0;
}

1.4 线程函数参数

1.4.1 传递临时对象作为线程参数

事实1:只要用临时构造的A类对象作为参数传递给线程,那么就一定能够在主线程执行完毕前把线程函数的第二个参数构建出来,从而确保及时detach()子线程也能够安全运行

  • 若传递int这种简单类型参数,建议都是值传递,不要用引用。防止节外生枝。
  • 如果是传递类对象,避免隐式类型转换。全部都在创建线程这一行就构建出临时对象来,然后在函数参数里用引用来接;否则系统还会多构造一次对象,浪费;

建议不使用detach(),只是用join():这样就不存在局部变量失效导致线程对内存的非法引用问题;

1.4.2 临时对象作为线程参数

线程id概念:id是个数字,每个线程(不管是主线程还是子线程)实际上都对应着一个数字,而且每个数字对应的这个数字都不同。

  • 不同的线程,它的线程id(数字)必然是不同;
  • 线程id可以用C++标准库里的函数来获取。std::this_thread::get_id()来获取;

1.4.3 传递类对象、智能指针作为线程参数

1. std::ref 函数

使用std::ref可以在模板传参的时候传入引用,否则无法传递

&是类型说明符, std::ref 是一个函数,返回 std::reference_wrapper(类似于指针)

用std::ref 是考虑到c++11中的函数式编程,如 std::bind.

2. unique_ptr详解-智能指针

https://blog.csdn.net/lemonxiaoxiao/article/details/108603916

3. 用成员函数指针做线程函数operator()

1.4.4 代码案例

#include <iostream>
// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。
// 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将
// WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。
#include <SDKDDKVer.h>
#include <stdio.h>
#include <tchar.h>
#include <vector>
#include <map>
#include <memory>
#include <string>
#include <thread>using namespace std;//void myprint(const int &i, char *pmybuf)
//void myprint(const int i, char *pmybuf)
void myprint01(const int i, const string &pmybuf)
{cout << i << endl;//分析认为,i并不是mvar的引用,实际是值传递,那么我们认为,即便主线程detach了子线程,那么子线程中用i值应该是安全的// //cout << pmybuf << endl;//指针在detach子线程时,绝对会有问题cout << pmybuf.c_str() << endl;return;
}int main01()
{//一:传递临时对象作为线程参数//(1.1)要避免的陷阱(解释1)//(1.2)要避免的陷阱(解释2)//事实1:只要用临时构造的A类对象作为参数传递给线程,那么就一定能够在主线程执行完毕前把线程函数的第二个参数构建出来,从而确保及时detach()子线程也能够安全运行//(1.3)总结//(a)若传递int这种简单类型参数,建议都是值传递,不要用引用。防止节外生枝。//(b)如果是传递类对象,避免隐式类型转换。全部都在创建线程这一行就构建出临时对象来,然后在函数参数里用引用来接;否则系统还会多构造一次对象,浪费;//终极结论://(c)建议不使用detach(),只是用join():这样就不存在局部变量失效导致线程对内存的非法引用问题;//二、临时对象作为线程参数继续讲,老师常用测试大发;//(2.1)线程id概念:id是个数字,每个线程(不管是主线程还是子线程)实际上都对应着一个数字,而且每个数字对应的这个数字都不同。//也就是,不同的线程,它的线程id(数字)必然是不同;//线程id可以用C++标准库里的函数来获取。std::this_thread::get_id()来获取;//三:传递类对象、智能指针作为线程参数//std::ref 函数//四:用成员函数指针做线程函数operator()int mvar = 1;int &mvary = mvar;char mybuf[] = "this is a test!";//thread mytobj(myprint, mvar, mybuf);//但是mybuf到底是什么时候转换成string。//事实上存在,mybuf都被回收了(main函数执行完了),系统才用mybuf去转string的可能性;thread mytobj(myprint01, mvar, string(mybuf));//我们这里直接将mybuf转换成string对象,这是 一个可以保证在线程中肯定有效的。//myobj.join();mytobj.detach();//子线程和主线程分别进行cout << "I Love China!" << endl;return 0;
}class A
{public://int m_i;mutable int m_i;//类型转换构造函数,可以把一个int转换成一个类A对象。A(int a) :m_i(a){//cout << "[A::A(int a)构造函数执行]" << this  << endl;cout << "[A::A(int a)构造函数执行]" << this << " thread = " << std::this_thread::get_id() << endl;}A(const A &a) :m_i(a.m_i){//cout << "[A::A(int a)拷贝构造函数执行]" << this << endl;cout << "[A::A(int a)拷贝构造函数执行]" << this << " thread = " << std::this_thread::get_id() << endl;}~A(){cout << "[A::A()析构函数执行]" << this << endl;cout << "[A::A()析构函数执行]" << this << " thread = " << std::this_thread::get_id() << endl;}void thread_work(int num)//来个参数{cout << "【子线程thread_work执行】" << this << " thread = " << std::this_thread::get_id() << endl;}void operator() (int num){cout << "【子线程()执行】" << this << " thread = " << std::this_thread::get_id() << endl;}
};void myprint02(const int i, const A &pmybuf)
{cout << &pmybuf << endl;//这里打印的是pmybuf对象的地址return;
}int main02()
{int mvar = 1;int mysecondpar = 12;//thread mytobj(myprint, mvar, mysecondpar);// 我们是希望mysecondpar转换成A类型对象传递给myprint的第二个参数thread mytobj(myprint02, mvar, A(mysecondpar));// 在创建线程的同事构造临时对象的方法传递参数是可行的;//mytobj.join();mytobj.detach();//子线程和主线程分别执行。cout << "I Love China!" << endl;return 0;
}void myprint(const A &pmybuf)
{cout << "子线程myprint2的参数地址是: " << &pmybuf << " thread = " << std::this_thread::get_id() << endl;//致命的问题居然是在子线程中构造的A类对象//用了临时对象后,所有的A类对象都在main()函数中就已经构建完毕了return;
}int main03()
{cout << "主线程id是: " << std::this_thread::get_id() << endl;int mvar = 1;//std::thread mytobj(myprtint, mvar);std::thread mytobj(myprint, A(mvar));//mytobj.join();mytobj.detach();//子线程和主线程分别执行。//cout << "I Love China!" << endl;return 0;
}void myprint03(const A &pmybuf)
{pmybuf.m_i = 199;//我们修改该值不会影响到main函数cout << "子线程myprint2的参数地址是: " << &pmybuf << " thread = " << std::this_thread::get_id() << endl;return;
}void myprint04(A &pmybuf)
{pmybuf.m_i = 199;//我们修改该值不会影响到main函数cout << "子线程myprint2的参数地址是: " << &pmybuf << " thread = " << std::this_thread::get_id() << endl;return;
}int main04()
{A myobj(10);//生成一个类对象;//std::thread mytobj(myprint02, myobj);//mytobj将类对象作为线程参数std::thread mytobj(myprint04, std::ref(myobj));mytobj.join();return 0;
}void myprint05(unique_ptr<int> &pzn)
{return;
}int main05()
{unique_ptr<int> myp(new int(100));std::thread mytobj(myprint05, std::move(myp));mytobj.join();//只能用join,不能用detach//mytobj.detach();//子线程和主线程分别执行。return 0;
}int main06()
{A myobj(10);//生成一个类对象std::thread mytobj(&A::thread_work, myobj, 15);//join和detach都可以 //std::thread mytobj(&A::thread_work, std::ref(myobj), 15);//只能用join//std::thread mytobj(&A::thread_work, &myobj, 15);//&myobj == std;:ref(myobj)mytobj.join();return 0;
}int main()
{A myobj(10);//生成一个类对象//std::thread mytobj(myobj, 15);std::thread mytobj(std::ref(myobj), 15);//不调用拷贝构造函数了,那么后续如果调用mytobj.detach()就不安全了;mytobj.join();return 0;
}

二、共享数据基础理解

2.1 创建多个线程

vector<thread> mythreads方法创建,方便管理。

#include <iostream>
// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。
// 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将
// WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。
#include <SDKDDKVer.h>
#include <stdio.h>
#include <tchar.h>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <list>
#include <mutex>using namespace std;//线程入口函数
void myprint01(int inum)
{cout << "myprint线程开始执行了,线程编号 = " << inum << endl;//....干各种事情 cout << "myprint线程结束执行了,线程编号 = " << inum << endl;return;
}int main()
{//一:创建和等待多个线程vector<thread> mythreads;//创建10个线程,线程入口函数统一使用 myprint。//a):多个线程执行顺序是乱的,跟操作系统内部对线程的运行调度机制有关;//b):主线程等待所有子线程运行结束,最后主线程结束,老师推荐这种join的写法,更容易写出稳定的程序;//c):咱们把thread对象放入到容器里面管理,看起来像个thread对象数组,这对我们依次创建大量的线程并对大量线程进行管理很方便。for (int i = 0; i < 10; i++){mythreads.push_back(thread(myprint01, i));//创建10个线程,同时这10个线程已经开始执行}for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter){iter->join();//等待10个线程都返回}cout << "I Love China!" << endl;//最后执行这句,整个进程退出return 0;
}

2.2 共享数据问题分析

例如:创建10个线程,对一个函数进行操作。

  • 只读的数据,是安全稳定的,不需要特别什么处理手段。直接读就可以;
  • 有读有写:2个线程写,8个线程读,如果代码没有特别的处理,那程序肯定崩溃。

最简单的不崩溃处理读的时候不能写,写的时候不能读2个线程不能同时写,8个线程不能同时读
写的动作分10小步;由于任务切换,导致各种诡异事情发生(最可能的诡异事情还是崩溃);

其他案例
数据共享: 北京–深圳 火车 T123,10个售票窗口 卖票, 1, 2 同时都要订 99座


vector<int> g_v = { 1, 2, 3 }; //共享数据//线程入口函数
void myprint02(int inum)
{cout << "id为" << std::this_thread::get_id() << "的线程 打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;return;
}int main()
{vector<thread> mythreads;for (int i = 0; i < 10; i++){mythreads.push_back(thread(myprint02, i));//创建10个线程,同时这10个线程已经开始执行}for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter){iter->join();//等待10个线程都返回}cout << "I Love China!" << endl;//最后执行这句,整个进程退出return 0;
}

2.3 共享数据的保护案例代码

  • 网络游戏服务器。两个自己创建的线程,一个线程收集玩家命令(用一个数字代表玩家发来的命令),并把命令数据写到一个队列中。
  • 另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家需要的动作;
  • vector,list,list跟vector类似。list:频繁地按顺序插入和删除数据时频率高。vector容器随机的插入和删除数据效率高。
  • 准备用成员函数作为线程函数的方法来写线程;
  • 代码化解决问题;引入一个C++解决多线程保护共享数据问题的第一个概念“互斥量”,往脑袋里记这个词;
// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。
// 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将
// WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。
#include <SDKDDKVer.h>
#include <stdio.h>
#include <tchar.h>
class A
{public://把收到的消息(玩家命令)入到一个队列的线程void inMsgRecvQueue(){for (int i = 0; i < 100000; ++i){cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;msgRecvQueue.push_back(i);//假设这个数字i就是我收到的命令,我直接弄到消息队列里边来;}}//把数据从消息队列中取出的线程:void outMsgRecvQueue(){for (int i = 0; i < 100000; ++i){if (!msgRecvQueue.empty()){//消息不为空int command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在;msgRecvQueue.pop_front();//移除第一个元素,但不返回;//这里就考虑度处理数据...//........}else{//消息队列为空cout << "outMsgEecvQueue()执行,但目前消息队列中为空" << i << endl;}}cout << endl;}private:std::list<int> msgRecvQueue;//容器,专门用于代表玩家给咱们发送过来的命令。
};int main()
{//三:共享数据的保护案例代码//网络游戏服务器。两个自己创建的线程,一个线程收集玩家命令(用一个数字代表玩家发来的命令),并把命令数据写到一个队列中。//  另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家需要的动作;//vector,list,list跟vector类似。list:频繁地按顺序插入和删除数据时频率高。vector容器随机的插入和删除数据效率高。//准备用成员函数作为线程函数的方法来写线程;//代码化解决问题;引入一个C++解决多线程保护共享数据问题的第一个概念“互斥量”,往脑袋里记这个词;//  A myobja;
//  std::thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);//第二个参数 引用,才能保证线程里 用的是同一个对象
//  std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);//  myInMsgObj.join();
//  myOutnMsgObj.join();std::cout<<"hello world!";return 0;
}

三、 互斥量的用法

保护共享数据,操作时,某个线程 用代码把共享数据锁住、操作数据、解锁, 其他想操作共享数据的线程必须等待解锁,锁定住,操作,解锁。

3.1互斥量的基本概念

一:互斥量(mutex)的基本概念

  1. 互斥量是个类对象。理解成一把锁,多个线程尝试用lock()成员函数赖加锁这把锁头,只有一个线程能锁定成功(成功的标志是lock()函数返回)
  2. 如果没锁成功,那么流程卡在lock()这里不断的尝试去锁这把锁头;解锁unlock()
  3. 互斥量使用要小心,保护数据不多也不少,少了,没达到保护效果,多了,影响效率

3.2互斥量的用法

3.1.1 lock(),unlock()

操作步骤:  先lock(),操作共享数据,unlock();
  • lock()和unlock()要成对使用,有lock()必然要有unlock(),每调用一次lock(),必然应该调用一次unlock();
  • 不应该也不允许调用1次lock()却调用2次unlock(),也不允许调用2次lock()却调用1次unlock(),这些非对称数量的调用都会导致代码不稳定甚至崩溃。
  • 有lock,忘记unlock的问题,非常难排查;
  • 为了防止大家忘记unlock(),引入了一个叫std::lock_guard的类模板:你忘记unlock不要紧,我替你unlock();
  • 学习过智能指针(unique_ptr<>):你忘记释放内存不要紧,我给你释放;(保姆)

3.1.2 std::lock_guard类模板

直接取代lock()和unlock();;也就是说,你用了lock_gruard之后,再不能使用lock()和unlock()

std::lock_guardstd::mutex sbguard(my_mutex);
sbguard随便起的对象名
lock_guard构造函数里执行了mutext::lock();
lock_guard析构函数里执行了mutext::unlock();

3.3代码案例

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <thread>
#include <list>
#include <mutex>using namespace std;class A
{public://把收到的消息(玩家命令)入到一个队列的线程void inMsgRecvQueue(){for (int i = 0; i < 100000; ++i){cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;//my_mutex.lock();{std::lock_guard<std::mutex> sbguard(my_mutex);msgRecvQueue.push_back(i);//假设这个数字i就是我收到的命令,我直接弄到消息队列里边来;}//my_mutex.unlock();//......//其他处理代码;}}bool outMsgLULProc(int &command){std::lock_guard<std::mutex> sbguard(my_mutex);//sbguard是随便起的对象名//lock_guard构造函数里执行了mutext::lock();//lock_guard析构函数里执行了mutext::unlock();//my_mutex.lock();if (!msgRecvQueue.empty()){//消息不为空command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在;msgRecvQueue.pop_front();//移除第一个元素,但不返回;//my_mutex.unlock();return true;}//my_mutex.unlock();return false;}//把数据从消息队列中取出的线程:void outMsgRecvQueue(){int command = 0;for (int i = 0; i < 100000; ++i){bool result = outMsgLULProc(command);if (result == true){cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;//可以考虑进行命令(数据)处理//.....}else{//消息队列为空cout << "outMsgEecvQueue()执行,但目前消息队列中为空" << i << endl;}}cout << endl;}private:std::list<int> msgRecvQueue;//容器,专门用于代表玩家给咱们发送过来的命令。std::mutex my_mutex;//创建一个互斥量(一把锁头)
};int main01()
{//保护共享数据,操作时,某个线程 用代码把共享数据锁住、操作数据、解锁,//其他想操作共享数据的线程必须等待解锁,锁定住,操作,解锁。//“互斥量”//一:互斥量(mutex)的基本概念//互斥量是个类对象。理解成一把锁,多个线程尝试用lock()成员函数赖加锁这把锁头,只有一个线程能锁定成功(成功的标志是lock()函数返回)//如果没锁成功,那么流程卡在lock()这里不断的尝试去锁这把锁头;解锁unlock()//互斥量使用要小心,保护数据不多也不少,少了,没达到保护效果,多了,影响效率//二:互斥量的用法//(2.1)lock(),unlock()//步骤:先lock(),操作共享数据,unlock();//lock()和unlock()要成对使用,有lock()必然要有unlock(),每调用一次lock(),必然应该调用一次unlock();//不应该也不允许调用1次lock()却调用2次unlock(),也不允许调用2次lock()却调用1次unlock(),这些非对称数量的调用都会导致代码不稳定甚至崩溃。//有lock,忘记unlock的问题,非常难排查;//为了防止大家忘记unlock(),引入了一个叫std::lock_guard的类模板:你忘记unlock不要紧,我替你unlock();//学习过智能指针(unique_ptr<>):你忘记释放内存不要紧,我给你释放;    保姆;//(2.2)std::lock_guard类模板:直接取代lock()和unlock();;也就是说,你用了lock_gruard之后,再不能使用lock()和unlock()A myobja;std::thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);//第二个参数 引用,才能保证线程里 用的是同一个对象std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);myInMsgObj.join();myOutnMsgObj.join();return 0;
}

C++多线程和并发-更新(互斥量的基本概念、使用、作用、案例)-2023/2/12相关推荐

  1. Linux下多线程同步方式之互斥量,信号量,条件变量

    // linux_thread_syn.cpp : 定义控制台应用程序的入口点. //#include "stdafx.h"//互斥量 #include <cstdio> ...

  2. windows 多线程(五) 互斥量(Mutex)

    参考:http://blog.csdn.net/morewindows/article/details/7470936 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行 ...

  3. java 互斥量_什么是Java中的互斥和信号量?主要区别是什么?

    问题 什么是Java中的互斥和信号量?主要区别是什么? #1 热门回答(127 赞) 不幸的是,每个人都错过了信号量和互斥量之间最重要的区别; "所有权"的概念. 信号量没有所有权 ...

  4. boost-同步-互斥量的概念

    互斥对象有利于实现多线程中数据的线程安全. 线程调用锁函数来获得互斥对象的所有权,调用对应的解锁函数来放弃所有权. 互斥量可以是递归或非递归的,并且可以同时把所有权赋给多个线程. Boost.Thre ...

  5. RTX5 | 互斥量01 - 互斥量的使用

    文章目录 一.前言 二.实验目的 三.API 3.1.osMutexAttr_t 3.2.osMutexNew 3.3.osMutexAcquire 3.4.osMutexGetOwner 3.5.o ...

  6. 秒杀多线程第九篇 经典线程同步总结 关键段 事件 互斥量 信号量

    前面<秒杀多线程第四篇一个经典的多线程同步问题>提出了一个经典的多线程同步互斥问题,这个问题包括了主线程与子线程的同步,子线程间的互斥,是一道非常经典的多线程同步互斥问题范例,后面分别用了 ...

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

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

  8. C/C++ 线程三种并发方式比较(传统互斥量加锁方式, no lock不加锁的方式, 原子函数方式)

    执行速度结果: 传统互斥量加锁方式 < no lock不加锁的方式 < 原子函数方式 正文如下: 最近编码需要实现多线程环境下的计数器操作,统计相关事件的次数.下面是一些学习心得和体会.不 ...

  9. OS: 读者写者问题(写者优先+LINUX+多线程+互斥量+代码)(转)

    一. 引子 最近想自己写个简单的 WEB SERVER ,为了先练练手,熟悉下在LINUX系统使用基本的进程.线程.互斥等,就拿以前学过的 OS 问题开开刀啦.记得当年学读者写者问题,尤其是写者优先的 ...

最新文章

  1. JS点击显示隐藏内容
  2. python 对象转dict_如何将python dict对象转换为java等效对象?
  3. 前端学习(479):html简介
  4. Clojure 学习入门(1) - 学习资料
  5. 南阳理工ACM之房间安排
  6. [phaser3入门探坑]使用phaser3制作山寨马里奥
  7. 自动弹出的html,360浏览器自动弹出网页怎么解决?
  8. NSIS静默安装VC运行库插件
  9. 人工智能在日常生活中的十大应用
  10. ardupilot 关于设备车Rover的学习《3》------模式控制
  11. python分支结构、循环结构
  12. 一周XX思考(第11期)
  13. IP座席接入系统方案
  14. 护眼灯到底有用吗?2022市面上这几款护眼灯真的能护眼
  15. android动态改变布局,Android 动态添加布局的两种方式
  16. AoCoder 1983 [AGC001E] BBQ Hard(组合数+dp)
  17. @Scheduled(cron = * * * * * *) cron表达式详解
  18. VScode 写完C程序无法调试运行
  19. 【无标题】互联网创新创业知识产权素养培养期末答案
  20. 网页前端设计-作业四(HTML5)

热门文章

  1. Linux磁盘管理和文件系统(相思相见知何日?此时此夜难为情)
  2. lmageNet 数据集简介
  3. I4mc-deep: 利用具有化学特性的深度学习方法,对 n4- 甲基胞嘧啶位点进行智能预测
  4. c语言程序设计学籍信息,C语言程序设计报告——学生学籍管理(报告).doc
  5. Windows常用快捷键及运行命令
  6. OSChina 周三乱弹 —— 听,BUG 哭泣的声音(多图)
  7. 0x8(0x80070035找不到网络路径)
  8. 电影赏析 001《全民目击》
  9. 一家披萨店不见客人,却能月入30万 !他们是怎么运营的呢?
  10. 活跃用户增长与饿了么口碑相差近3倍,美团的下一步去哪儿?