多线程笔记-创新互联
- 设计模式
- 单例设计模式(高频使用)
- 单例模式共享数据问题分析解决解决
- std::call_once()
- std::condition_variable ,wait(),notify_one,notify_all();
- async future packaged_task promise
- future其他成员函数,shared_future atomic
- 深入谈谈async
- window临界区
- 自动析构技术
- recursive_mutex递归的独占互斥量
- 带超时的互斥量std::timed_mutex
大的项目分解成一个个小的模块。
单例设计模式(高频使用)整个项目中,有某个或者某些特殊的类,属于类的对象,只能创建一个,多的创建不了。
// 单例模式示范代码
class MyCas
{private:
MyCas() {} //私有化构造函数,避免该类创建多个
private:
static MyCas* m_instance;
static mutex m_mutex;
public:
static MyCas* GetInstance()
{if (m_instance == NULL) //双重锁定,双重检查,保证只有当m_instance不为空的时候才加锁,提高效率
{unique_lockmymutex(m_mutex);
if (m_instance == NULL)
{m_instance = new MyCas();
static CCarhuishouMyCas cl; //生命周期直到程序结束,以至于当这个对象释放时调用析构函数释放new出来的对象
}
}
return m_instance;
}
class CCarhuishouMyCas //类中套类来释放new出来的对象
{public:
~CCarhuishouMyCas()
{if (MyCas::m_instance)
{delete MyCas::m_instance;
MyCas::m_instance = NULL;
}
}
};
};
MyCas* MyCas::m_instance = NULL;
mutex MyCas::m_mutex;
int main()
{MyCas* p_a = MyCas::GetInstance();
return 0;
}
单例模式共享数据问题分析解决解决若数据是只读不写的,就不需要加锁保护
面临的问题:需要多个线程中去创建单例类,这时有可能会出现GetInstance()这种成员函数要互斥
解决方法:就是要改进getinstance函数
//改进前
static MyCas* GetInstance()
{if (m_instance == NULL)
{m_instance = new MyCas();
static CCarhuishouMyCas cl; //生命周期直到程序结束,以至于当这个对象释放时调用析构函数释放new出来的对象
}
return m_instance;
}
//改进后
static MyCas* GetInstance()
{if (m_instance == NULL) //双重锁定,双重检查,保证只有当m_instance不为空的时候才加锁,提高效率
{unique_lockmymutex(m_mutex);
if (m_instance == NULL)
{m_instance = new MyCas();
static CCarhuishouMyCas cl; //生命周期直到程序结束,以至于当这个对象释放时调用析构函数释放new出来的对象
}
}
return m_instance;
}
std::call_once()c++11引入的函数,该函数的第二个参数是一个函数名a();
call_once()功能可以保证函数a()只被调用一次
call_once()具备互斥量的能力,并且效率上比互斥量消耗的资源少。
它主要是通过绑定一个std::one_flag结构(标志位实现的)。
下面示范通过callz_once()改造单例模式,替代其互斥量。
// 单例模式示范代码
class MyCas
{private:
MyCas() {} //私有化构造函数,避免该类创建多个
private:
static MyCas* m_instance;
static once_flag g_flag;
public:
static void CreateInstance()//这里表示只被调用一次的函数
{m_instance = new MyCas();
static CCarhuishouMyCas cl; //生命周期直到程序结束,以至于当这个对象释放时调用析构函数释放new出来的对象
}
static MyCas* GetInstance()
{call_once(g_flag, CreateInstance);
return m_instance;
}
class CCarhuishouMyCas //类中套类来释放new出来的对象
{public:
~CCarhuishouMyCas()
{if (MyCas::m_instance)
{delete MyCas::m_instance;
MyCas::m_instance = NULL;
}
}
};
};
MyCas* MyCas::m_instance = NULL;
once_flag MyCas:: g_flag;
int main()
{MyCas* p_a = MyCas::GetInstance();
return 0;
}
std::condition_variable ,wait(),notify_one,notify_all();std::condition_variable
实际上是一个类,是需要和互斥量mutex配合使用,使用的时候要生成这个类
wait()
用来等待一个东西,当第二个参数返回值是false时候,wait()将解锁互斥量,并且堵塞到本行;堵塞到其他线程调用notify_one()成员函数为止如果没有第二个参数,那默认效果和第二个参数为false一样,立即堵塞。
当其他线程调用notify_one()将wait唤醒后,wait就解除阻塞状态。然后wait会不断尝试重新获取互斥量的锁,如果获取不到wait会卡在这里等待锁的获取,当获取到锁后,
如果wait第二个参数是lamda表达式,会继续执行lamda表达式,此时如果为true,继续执行(此时还是加锁状态
如果此时没有第二个参数,则默认是true;往下继续执行。一般来说wait()需要第二个参数,防止虚假唤醒
notify_all() 则是唤醒全部wait(),在下面示例代码中,其实notify_one 和notify_all效果是一样的。
#include#include#include#include#includeusing namespace std;
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 10000; i++)
{cout<< "inmsgRecvqueue执行,插入一个元素"<< i<guard1(my_mutex);
msgRecvQueue.push_back(i);
my_cond.notify_one();//尝试把wait的线程唤醒,不一定能唤醒,如果线程此时没有卡在wait哪里的话
}
}
void outMsgRecvQueue()
{while(1)
{unique_lockguard1(my_mutex);
my_cond.wait(guard1, [this] {if (!msgRecvQueue.empty())
return true;
else
return false;
});
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
guard1.unlock(); //因为用的是类模板此时还是锁着的,可以手动解锁,避免卡太长时间
//......一些操作
}
}
public:
listmsgRecvQueue;
mutex my_mutex;
condition_variable my_cond;
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
async future packaged_task promise一 std::async
async是一个类模板,用于启动一个异步任务(即开启一个线程来执行对应的线程入口函数),然后会返回一个futrue对象(里面包括了该线程返回的结果,可以通过futrue对象的成员函数get()来获取结果) (future对象里面会保存一个值,在将来的某个时刻可以得到)
class A
{public:
int mythread(int k )
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
};
int mythread()
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
int main()
{int tm = 12;
class A a;
cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
futureresult = std::async(&A::mythread,&a,tm);
cout<< ".........."<< endl;
cout<< result.get()<< endl; //如果线程没执行完,线程会卡在这里等待线程执行完毕返回结果。
cout<< "!!!!!!"<< endl;
return 0;
}
此外,我们可以通过额外向async()传递一个参数,该参数类型是launch类(枚举类型)来达到一些效果
1,std::launch::deferred:表示线程如果函数调用延迟到std::future的wait()或者get()函数调用是才执行,且如果不调用wait()和get(),该线程直接不执行。即使调用wait()和get()也不创建新线程,都是主线程里面执行线程入口函数。
futureresult = std::async(std::launch::deferred,&A::mythread,&a,tm);
2,std::launch::async:表示在调用async函数的时候就开始创建线程
futureresult = std::async(std::launch::async,&A::mythread,&a,tm);
二 std::packaged_task
packaged_task是一个类模板,模板参数是各种可调用对象,通过packaged_task包装起来,方便将来作为线程入口函数来调用
int mythread(int k)
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt(mythread);
thread t1(ref(mypt), 1); //ref()指用引用的方式传递
t1.join();
std::futureresult = mypt.get_future();
cout<< result.get()<< endl;
cout<< "!!!!!"<< endl;
return 0;
}
//改为lamda表达式
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt([](int k) {
cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
});
thread t1(ref(mypt), 1); //ref()指用引用的方式传递
t1.join();
std::futureresult = mypt.get_future();
cout<< result.get()<< endl;
cout<< "!!!!!"<< endl;
return 0;
}
//也可以直接调用,但是就都在主线程中执行了。
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt([](int k) {
cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
});
mypt(100);
std::futureresult = mypt.get_future();
cout<< result.get()<< endl;
cout<< "!!!!!"<< endl;
return 0;
}
//和容器关联packaged_task
vector>mytasks;
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt([](int k) {
cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
});
mytasks.push_back(std::move(mypt));//通过std::move,可以避免不必要的拷贝操作,是为性能而生,将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。
packaged_taskmypt2;
auto it = mytasks.begin();
mypt2 = std::move(*it);
mytasks.erase(it); //删除第一个元素,迭代器已经失效了,不能再使用it;
mypt2(123);
futureresult = mypt2.get_future();
cout<< result.get()<< endl;
return 0;
}
二 std::promise
promise
可以能够在某个线程中给它赋值,然后我们可以在其他线程中,把这个值给取出来
void mythread(promise& tmmp, int calc)
{calc++;
calc *= 10;
std::chrono::milliseconds dur(5000);
std::this_thread::sleep_for(dur);
int result = calc;
tmmp.set_value(result);
return;
}
void mythread2(futuretmmp)
{auto k = tmmp.get();
cout<< "thread2 "<< k<< endl;
return;
}
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
promisemyprom;
thread t1(mythread, ref(myprom), 12);
t1.join();
thread t2(mythread2,myprom.get_future());
t2.join();
return 0;
}
future其他成员函数,shared_future atomicfuture_status 有三种状态:timeout ready deferred
class A
{public:
int mythread(int k)
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
cout<< k<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
};
int main()
{int tm = 12;
class A a;
cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
futureresult = std::async( &A::mythread, &a, tm);
//futureresult = std::async(std::launch::deferred, &A::mythread, &a,tm);
cout<< ".........."<< endl;
// cout<< result.get()<< endl; //如果线程没执行完,线程会卡在这里等待线程执行完毕返回结果。
future_status status = result.wait_for(std::chrono::seconds(6));
if (status == future_status::timeout) //超时,指的的等待时间结束后,线程还没有运行完
{cout<< "超时,线程还没有执行完"<< endl;
}
else if (status == future_status::ready) // 准备好,表示等待时间结束后,线程已经运行完了
{cout<< "线程已经执行完毕"<< endl;
}
else if (status == future_status::deferred) //如果async第一个参数是deferred,该条件成立
{cout<< "线程被延迟执行"<< endl;
cout<< result.get()<< endl;
}
cout<< "!!!!!!"<< endl;
return 0;
}
std::shared_future (主要是为了解决future_get()只能被调用一次的问题(这里的get其实这里是转移的意思),适合用于多个线程使用同一个future,本质就是把get()的语义由转移变为拷贝)
int mythread(int k)
{cout<< "threadid"<< std::this_thread::get_id()<< endl;
std::chrono::milliseconds dura(5000);
std::this_thread::sleep_for(dura);
return 5;
}
int mythread2(shared_future& g)
{cout<< "get_threadid"<< std::this_thread::get_id()<< endl;
cout<< g.get()<< endl;
return 5;
}
int main()
{cout<< "mainthreadid"<< std::this_thread::get_id()<< endl;
packaged_taskmypt(mythread);
thread t1(ref(mypt), 1); //ref()指用引用的方式传递
t1.join();
std::futureresult = mypt.get_future();
std::shared_futureresult2(move(result));//或者 std::shared_futureresult2(result.share());
//或者直接std::shared_futureresult2(mypt.get_future());
if (result.valid()) //可用于判断result是否可以被get,就是是否为空
{cout<< "result有效"<< endl;
}
thread t2(mythread2, ref(result2)); //ref()指用引用的方式传递
thread t3(mythread2, ref(result2)); //ref()指用引用的方式传递
thread t4(mythread2, ref(result2)); //ref()指用引用的方式传递
t2.join();
t3.join();
t4.join();
cout<< "!!!!!"<< endl;
return 0;
}
原子操作:: std::atomic(比加锁效率高,频繁加锁影响效率)
原子操作可以理解为一种不需要互斥量加锁(无锁)技术的多线程并发编程方式
原子操作:在多线程中不会被打断的程序执行片段
原子操作和互斥量的使用场景不同(原子:即不可分割)
互斥量加锁一般针对一个代码段,而原子操作针对的一般都是一个变量,而不是一个代码段
一般用来处理变量的简单操作
注意:一般的atomic原子操作,针对++,–,+=,&=,|=,^=,-=是支持的。其他的可能不支持,遇到这种情况,可以写一小段代码进行测试
原子操作不支持拷贝构造函数
例如:
以下这些都是不被允许的
atomic<>atm=0;
atomicatm2=atm
autoatm3=atm
若实在想复制,可以调用load()函数
load():以原子的方式读atomic的值
atomicatm2(atm.load())
autoatm3(atm.load())
store():以原子方式写入内容
atm2.store(12) //atm2=12
下面将用互斥量和锁进行一个对比,并结合c++时间戳来比较两者之间的效率差距
//使用原子操作需要时间
#include#include#include#include#includeusing namespace std;
atomicm_count = 0;
void mythread()
{for (int i = 0; i< 20000000; i++)
{m_count++;
}
}
int main()
{auto tpbegin = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
thread t1(mythread);
thread t2(mythread);
t1.join();
t2.join();
auto tpend = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
cout<<"累计 40000000所需时间:"<< tpend.count()- tpbegin.count()<<"ms"<< endl;
cout<< m_count<< endl;
return 0;
}
累计 40000000所需时间:1098ms
40000000
//使用互斥量需要时间
#include#include#include#include#includeusing namespace std;
int m_count = 0;
mutex m_gmutex;
void mythread()
{for (int i = 0; i< 20000000; i++)
{m_gmutex.lock();
m_count++;
m_gmutex.unlock();
}
}
int main()
{auto tpbegin = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
thread t1(mythread);
thread t2(mythread);
t1.join();
t2.join();
auto tpend = chrono::duration_cast(chrono::high_resolution_clock::now().time_since_epoch());
cout<<"累计 40000000所需时间:"<< tpend.count()- tpbegin.count()<<"ms"<< endl;
cout<< m_count<< endl;
return 0;
}
累计 40000000所需时间:6408ms
40000000
综合上述,在累计40000000操作后,原子操作比互斥操作快6倍左右,随着次数越来越多,差距会越大。
深入谈谈asyncasync有两个参数
std::launch::deferred【延迟调用】
std::launch::async【强制创建一个线程】
用thread就说明创建线程,对于async来说,一般不叫它创建线程(虽然效果是能够创建线程),一般叫它为创建一个异步任务
async和thread最明显的不同是,async有时候并不创建线程(用std::launch::deferred参数的时候)
(a)当使用std::launch::deferred参数的时候,并不会创建新的线程,他会延迟到future对象调用.get()或.wait()的时候才执行入口函数,如果没 有调用.get()或.wait(),甚至连入口函数都不执行
(b)当使用std::launch::async时候,会强制这个异步任务在新线程上面执行,意味着,系统必须要创建出一个新线程来运行入口函数
©当使用std::launch::deferred|std::launch::async,系统会根据情况自己选择一种执行
(d)若不使用参数,效果和std::launch::deferred|std::launch::async一样的,系统会自行决定会同步调用还是异步调用
所谓的自行决定是异步(创建新线程)还是同步(不创建新线程)方式运行
(1)async和thread区别
(a)使用thread方式创建线程,不容易拿到线程的返回值(要拿可能需要使用全局变量的方式)
使用async创建异步任务,可以很轻松的通过future获得线程的返回值
(b)用thread创建线程,如果系统资源紧张,有可能会创建线程失败,容易导致程序崩溃。
使用async一般不会崩溃,因为如果系统资源紧张的时候,async这种不加额外参数的调用就不会创建新线程,而是后续调用result.get()来请求结果,判断是同步还是异步执行。
经验:一个程序里面,线程数量不宜超过100-200
(2)当使用async,不用参数的时候那如果判断是同步还是异步,需要使用future_status来判断
future_status status = result.wait_for(std::chrono::seconds(0));
if (status == future_status::timeout) //超时,指的的等待时间结束后,线程还没有运行完
{cout<< "超时,线程还没有执行完"<< endl;
}
else if (status == future_status::ready) // 准备好,表示等待时间结束后,线程已经运行完了
{cout<< "线程已经执行完毕"<< endl;
}
//上面两种情况说明都是异步执行,从async创建时候线程就执行了
else if (status == future_status::deferred) //如果async第一个参数是deferred,该条件成立
{cout<< "线程被延迟执行"<< endl;
cout<< result.get()<< endl; //如果是这种情况,只有当.get()是函数才开始跑,且在同一个线程里面执行。
}
window临界区#include#include#include#include#includeusing namespace std;
#define _WINDOWSJQ_ //开关
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 100; i++)
{cout<< "inmsgRecvqueue执行,插入一个元素"<< i<< endl;
#ifdef _WINDOWSJQ_
EnterCriticalSection(&my_winsec);
msgRecvQueue.push_back(i);
LeaveCriticalSection(&my_winsec);
#else
unique_lockguard1(my_mutex);
msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
}
}
void outMsgRecvQueue()
{while (1)
{if (!msgRecvQueue.empty())
{if (!msgRecvQueue.empty())
{#ifdef _WINDOWSJQ_
EnterCriticalSection(&my_winsec);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
LeaveCriticalSection(&my_winsec);
#else
unique_lockguard1(my_mutex);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_
}
}
}
}
A()
{#ifdef _WINDOWSJQ_
InitializeCriticalSection(& my_winsec);//windows中的临界区使用必须初始化
#endif // _WINDOWSJQ_
}
public:
listmsgRecvQueue;
mutex my_mutex;
condition_variable my_cond;
#ifdef _WINDOWSJQ_
CRITICAL_SECTION my_winsec;//windows中的临界区,非常类似于互斥量mutex
#endif // _WINDOWSJQ_
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
在同一线程中,windows中可以多次进入临界区。但是调用几次临界区,就需要调用出几次临界区。而在c++11中,不允许同一个线程lock一个互斥量多次,否则报异常。
自动析构技术下面把win临界区封装成一个类,本类自动释放临界区,类似mutex的lock_guard
这种叫做RAII类,即(Resource Acquisition is initialization) “资源获取即初始化”,容器,智能指针都是raii类
#include#include#include#include#includeusing namespace std;
#define _WINDOWSJQ_ //开关
//本类自动释放临界区,类似mutex的lock_guard
//这种叫做RAII类,即(Resource Acquisition is initialization) “资源获取即初始化”,容器,智能指针都是raii类
class winlock
{public:
winlock(CRITICAL_SECTION* winsec)
{my_winsec = winsec;
EnterCriticalSection(my_winsec);
}
~winlock()
{LeaveCriticalSection(my_winsec);
}
CRITICAL_SECTION *my_winsec;
};
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 100; i++)
{cout<< "inmsgRecvqueue执行,插入一个元素"<< i<< endl;
#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
msgRecvQueue.push_back(i);
#else
unique_lockguard1(my_mutex);
msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
}
}
void outMsgRecvQueue()
{while (1)
{if (!msgRecvQueue.empty())
{if (!msgRecvQueue.empty())
{#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#else
unique_lockguard1(my_mutex);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_
}
}
}
}
A()
{#ifdef _WINDOWSJQ_
InitializeCriticalSection(&my_winsec);//windows中的临界区使用必须初始化
#endif // _WINDOWSJQ_
}
public:
listmsgRecvQueue;
mutex my_mutex;
condition_variable my_cond;
#ifdef _WINDOWSJQ_
CRITICAL_SECTION my_winsec;//windows中的临界区,非常类似于互斥量mutex
#endif // _WINDOWSJQ_
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
recursive_mutex递归的独占互斥量主要是为了解决重复lock会报错的问题
std::mutex:独占互斥量,自己lock了,别人lock不了
recursive_mutex:递归的独占互斥量:允许同一个线程,同一个互斥量多次lock。
#include#include#include#include#includeusing namespace std;
//#define _WINDOWSJQ_ //开关
//本类自动释放临界区,类似mutex的lock_guard
//这种叫做RAII类,即(Resource Acquisition is initialization) “资源获取即初始化”,容器,智能指针都是raii类
class winlock
{public:
winlock(CRITICAL_SECTION* winsec)
{my_winsec = winsec;
EnterCriticalSection(my_winsec);
}
~winlock()
{LeaveCriticalSection(my_winsec);
}
CRITICAL_SECTION *my_winsec;
};
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 100; i++)
{cout<< "inmsgRecvqueue执行,插入一个元素"<< i<< endl;
#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
msgRecvQueue.push_back(i);
#else
unique_lockguard1(my_mutext);//这里相当与lock了三次
testfun2();
msgRecvQueue.push_back(i);
#endif // _WINDOWSJQ_
}
}
void outMsgRecvQueue()
{while (1)
{if (!msgRecvQueue.empty())
{if (!msgRecvQueue.empty())
{#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#else
unique_lockguard1(my_mutext);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_
}
}
}
}
A()
{#ifdef _WINDOWSJQ_
InitializeCriticalSection(&my_winsec);//windows中的临界区使用必须初始化
#endif // _WINDOWSJQ_
}
public:
listmsgRecvQueue;
condition_variable my_cond;
std::recursive_mutex my_mutext;
#ifdef _WINDOWSJQ_
CRITICAL_SECTION my_winsec;//windows中的临界区,非常类似于互斥量mutex
#endif // _WINDOWSJQ_
void testfun1()
{lock_guardsbguard(my_mutext);
//干一些事情
}
void testfun2()
{lock_guardsbguard(my_mutext);
//干一些事情
testfun1();
}
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
但是这种多次锁效率肯定比mutex效率差,如果通过改代码能改成只lock一次最好。
带超时的互斥量std::timed_mutexstd::timed_mutex带超时的独占互斥量
有两个参数:
try_lock_for();等待一段时间,无论有没有拿到锁,程序都走下去
try_lock_until();参数是一个未来的时间点,在这个未来的时间没到的时间内,如果拿到锁,就走下去,到时间没拿到,也走下去。
try_lock_for()示例
#include#include#include#include#includeusing namespace std;
//#define _WINDOWSJQ_ //开关
//本类自动释放临界区,类似mutex的lock_guard
//这种叫做RAII类,即(Resource Acquisition is initialization) “资源获取即初始化”,容器,智能指针都是raii类
class winlock
{public:
winlock(CRITICAL_SECTION* winsec)
{my_winsec = winsec;
EnterCriticalSection(my_winsec);
}
~winlock()
{LeaveCriticalSection(my_winsec);
}
CRITICAL_SECTION *my_winsec;
};
class A {public:
void inMsgRecvQueue()
{for (int i = 0; i< 100; i++)
{
#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
msgRecvQueue.push_back(i);
#else
chrono::milliseconds timeout(100);;
if (my_mutext.try_lock_for(timeout))//等待100ms来获取锁
{cout<< "inmsgRecvqueue执行,插入一个元素"<< i<< endl;
msgRecvQueue.push_back(i);
my_mutext.unlock(); //拿到锁用完了记得释放
}
else
{//没有拿到锁休眠100ms
chrono::milliseconds dur(100);
std::this_thread::sleep_for(dur);
}
#endif // _WINDOWSJQ_
}
}
void outMsgRecvQueue()
{while (1)
{if (!msgRecvQueue.empty())
{if (!msgRecvQueue.empty())
{#ifdef _WINDOWSJQ_
winlock win(&my_winsec);
int k = msgRecvQueue.front();
cout<< "取出"<< k<< endl;
msgRecvQueue.pop_front();
#else
unique_lockguard1(my_mutext);
chrono::milliseconds dur(1000);
std::this_thread::sleep_for(dur);
int k = msgRecvQueue.front();
cout<< k<< endl;
msgRecvQueue.pop_front();
#endif // _WINDOWSJQ_
}
}
}
}
A()
{#ifdef _WINDOWSJQ_
InitializeCriticalSection(&my_winsec);//windows中的临界区使用必须初始化
#endif // _WINDOWSJQ_
}
public:
listmsgRecvQueue;
timed_mutex my_mutext;
#ifdef _WINDOWSJQ_
CRITICAL_SECTION my_winsec;//windows中的临界区,非常类似于互斥量mutex
#endif // _WINDOWSJQ_
};
int main()
{A myojia;
std::thread myOutmsgobj(&A::outMsgRecvQueue, &myojia);
std::thread myInmsgobj(&A::inMsgRecvQueue, &myojia);
myOutmsgobj.join();
myInmsgobj.join();
return 0;
}
try_lock_until()示例
上面替换即可。
chrono::milliseconds timeout(100);
if (my_mutext.try_lock_until(chrono::steady_clock::now()+timeout))//等待100ms来获取锁
{cout<< "inmsgRecvqueue执行,插入一个元素"<< i<< endl;
msgRecvQueue.push_back(i);
my_mutext.unlock(); //拿到锁用完了记得释放
}
else
{//没有拿到锁休眠100ms
chrono::milliseconds dur(100);
std::this_thread::sleep_for(dur);
}
recursive_mutex也有一个类似的std::recursive_timed_mutex
可以多次加锁。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网站题目:多线程笔记-创新互联
网页地址:http://hbruida.cn/article/pcceg.html