头文件:#include < thread >
- std::thread的集中使用方式
using namespace std;
using namespace std::chrono;
#include <thread>
#include <chrono>
void fun1(int n);
void fun2(int &n);
class test_thread
{
public:
void test_fun()
{
for (int i = 0; i < 5; ++i)
{
std::cout<<"thread 3 \n";
n++;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void test_fun(int k)
{
for (int i = 0; i < 5; ++i)
{
std::cout<<"thread 3 "<<k<<std::endl;
n++;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int n = 0;
};
class test_baz
{
public:
void operator()()
{
for (int i = 0; i < 5; ++i)
{
std::cout<<"thread baz \n";
n++;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int n = 0;
};
class CCThread
{
public:
CCThread();
~CCThread();
public:
void call_thread();
void atomic_fun();
void call_mutex(std::string &url);
private:
std::atomic_int acnt;
int cnt;
std::map<std::string,std::string> g_pages;
std::mutex g_pages_mtx;
};
void fun1(int n)
{
for (int i = 0; i < 5; ++i)
{
std::cout<<"thread 1 \n";
n++;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); //delay 100 ms
std::cout<<"thread 1 end\n";
}
}
void fun2(int &n)
{
for (int i = 0; i < 5; ++i)
{
std::cout<<"thread 2 \n";
n++;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void fun_getid()
{
const auto start = std::chrono::high_resolution_clock ::now(); //get current time
std::thread::id this_id = std::this_thread::get_id();
std::this_thread::sleep_for(std::chrono::milliseconds(500)); //delay 500ms
std::cout<<"fun_getid "<<this_id<<" \n";
const auto end = std::chrono::high_resolution_clock ::now(); //get current time
std::chrono::duration<double,std::milli> elapsed = end-start;
auto wake_time = std::chrono::system_clock::now() + std::chrono::seconds(5);
std::this_thread::sleep_until(wake_time); //sleep_until适用于需要在未来的某个特定时间点终止线程执行的场景
}
CCThread::CCThread(){}
CCThread::~CCThread(){}
void CCThread::call_thread()
{
int n = 0;
test_thread _test;
test_baz _baz;
std::thread _thread1; // not a thread;
std::thread _thread2(fun1,n+1); // pass param by value
std::thread _thread3(fun2,std::ref(n)); //pass param by reference
std::thread _thread4(std::move(_thread3)); //t4 is now running fun2 ,t3 is not a thread
std::thread _thread5(&test_thread::test_fun,&_test);
std::thread _thread6(_baz);
/******************************* reload func on thread called ************************************/
//重载函数调用方式一
using func = void(test_thread::*)(); //no param passed
func _func = &test_thread::test_fun;
std::thread _thread7(std::bind(_func,&_test));
using func = void(test_thread::*)(int); //have a int param passed
func _func = &test_thread::test_fun;
std::thread _thread7(std::bind(_func,&_test,5));
//重载函数调用方式二
void(test_thread::*func)()=&test_thread::test_fun;
std::thread _thread7(std::bind(func,&_test));
void(test_thread::*func)(int)=&test_thread::test_fun;
std::thread _thread7(std::bind(func,&_test,8));
/******************************* reload func on thread called ************************************/
// _thread2.join();
// _thread3.join();
// _thread4.join();
// _thread5.join();
// _thread6.join();
_thread7.join();
}
- 互斥锁 数据竞争与资源互斥
std::mutex mtx;
void shared_printf(string msg,int id)
{
//mtx.lock(); //当互斥对象内部资源出现异常时,此处将造成死锁
//cout<<"__FUNCTION__ msg:"<<msg<<" id:"<<id<<endl;
//mtx.unlock();
std::lock_guard<std::mutex> guard(mtx); //没有更安全的保护全局对象 cout
cout<<"__FUNCTION__ msg:"<<msg<<" id:"<<id<<endl;
}
void operate_func()
{
for (size_t i = 0; i < 50; i++)
shared_printf("operate_func",i);
}
void test_mutex_operate()
{
std::thread t2(operate_func);
for (size_t i = 0; i < 50; i++)
shared_printf("test_thread_manage",i);
t2.join();
}
- 类中使用互斥量安全保护
class LockFile
{
private:
/* data */
std::mutex m_mtx;
ofstream of;
public:
void shared_printf(string msg,int id)
{
std::lock_guard<std::mutex> guard(m_mtx); //没有更安全的保护全局对象 cout
of<<"LockFile shared msg: "<<msg<<" id:"<<id<<endl;
}
//std::ofstream& get_stream(){return of;} //错误做法,打破了m_mtx互斥对象的保护of的规则
//也不能作为函数参数传递出去
public:
LockFile(/* args */){of.open("log.txt");}
~LockFile(){}
};
void func_1(LockFile &log)
{
for (size_t i = 0; i < 50; i++)
log.shared_printf("operate_func",i);
}
void test_mutex_class()
{
LockFile log;
std::thread t1(func_1,std::ref(log));
for (size_t i = 0; i < 50; i++)
log.shared_printf("test_thread_manage",i);
t1.join();
}
- 死锁问题及处理方式
#include <list>
#include <limits.h>
class MultiThread
{
public:
std::mutex m_mtx;
//std::lock_guard m_lock;
private:
list<int> m_list;
public:
int do1;
int do2;
public:
void ListPush();
bool ListPop();
void Swap(MultiThread &mt1, MultiThread &mt2);
};
void swap(int do1,int do2) {}
void MultiThread::ListPush()
{
std::lock_guard<std::mutex> guard_mtx(m_mtx);
m_list.push_back(1);
}
bool MultiThread::ListPop()
{
std::lock_guard<std::mutex> guard_mtx(m_mtx);
bool ret=std::find(m_list.begin(),m_list.end(),1) != m_list.end();
return ret;
}
- 死锁问题,解决方案
void MultiThread::Swap(MultiThread& mt1, MultiThread& mt2)
{
if (&mt1 == &mt2)
return;
//用 std::lock 去锁lhs.m或rhs.m时,可能会抛出异常;这种情况下,异常会传播
//到 std::lock 之外。当 std::lock 成功的获取一个互斥量上的锁,并且当其尝试
//从另一个互斥 量上再获取锁时,就会有异常抛出,第一个锁也会随着异常的产生而自动释放,所以std::lock 要么将两个锁都锁住,要不一个都不锁
std::lock(mt1.m_mtx,mt2.m_mtx); //调用 std::lock() 锁住 两个互斥量
std::lock_guard<std::mutex> lock_a(mt1.m_mtx, std::adopt_lock);
std::lock_guard<std::mutex> lock_b(mt2.m_mtx, std::adopt_lock);
swap(mt1.do1,mt2.do2);
//下面的代码与上述功能相同
//可将 std::adopt_lock 作为第二 个参数传入构造函数,对互斥量进行管理
//可以将 std::defer_lock 作为第二个参数传递进 去,表明互斥量应保持解锁状态
//就可以被 std::unique_lock 对象(不是互斥量)的 lock()函数的所获取,或传递 std::unique_lock 对象到 std::lock() 中
//std::unique_lock 会占用比较多的空间,并且比 std::lock_guard 稍慢一些。保证灵活性 要付出代价,这个代价就是允许 std::unique_lock 实例不带互斥量
std::unique_lock<std::mutex> lock_c(mt1.m_mtx, std::defer_lock); // 1
std::unique_lock<std::mutex> lock_d(mt2.m_mtx,std::defer_lock); // 1 std::def_lock 留下未上锁的互斥量
std::lock(lock_c,lock_d); // 2 互斥量在这里上锁
swap(mt1.do1, mt2.do2);
}
- 避免死锁问题
1:避免嵌套锁,
一个线程已获得一个锁时,再别去获取第二个;因为每个线程只持有一个锁,锁上就不会产生死锁 当你需要获取多个锁,使用一个 std::lock 来做这件事(对获取锁的操作上锁),避免产生死锁 。
2:避免在持有锁时调用用户提供的代码
3:使用固定顺序获取锁
当硬性条件要求你获取两个以上(包括两个)的锁,并且不能使用 std::lock 单独操作来获取它们;那么最好在每个线程上,用固定的顺序获取它们获取它们(锁) 提供一种避免死锁的方式,定义遍历的顺序,所以一个线程必须先锁住A才能获取B的 锁,在锁住B之后才能获取C的锁。 这将消除死锁发生的可能性,在不允许反向遍历的列表 上。类似的约定常被用来建立其他的数据结构
4.使用层次锁来避免死锁 如下代码:
class hierarchical_mutex
{
std::mutex internal_mutex;
unsigned long const hierarchy_value;
unsigned long previous_hierarchy_value;
static thread_local unsigned long this_thread_hierarchy_value; // 1
public:
void check_for_hierarchy_violation()
{
if(this_thread_hierarchy_value <= hierarchy_value) // 2
{
throw std::logic_error("mutex hierarchy violated");
}
}
void update_hierarchy_value()
{
previous_hierarchy_value = this_thread_hierarchy_value; // 3
this_thread_hierarchy_value=hierarchy_value;
}
public:
explicit hierarchical_mutex(unsigned long value): hierarchy_value(value), previous_hierarchy_value(0)
{
}
void lock()
{
//会通过 check_for_hierarchy_vilation()②的检查。在这种检查方式下,lock()代表内部互斥锁已被锁住 ④。一旦成功锁住,你可以更新层级值了
check_for_hierarchy_violation();
internal_mutex.lock(); // 4
update_hierarchy_value(); // 5
}
void unlock()
{
this_thread_hierarchy_value=previous_hierarchy_value; // 6
internal_mutex.unlock();
}
bool try_lock()
{
check_for_hierarchy_violation();
if(!internal_mutex.try_lock()) // 7
return false;
update_hierarchy_value();
return true;
}
};
只有thread_local关键字修饰的变量具有线程周期(threadduration),这些变量(或者说对象)在线程开始的时候被生成(allocated),在线程结束的时候被销毁(deallocated)并且每一个线程都拥有一个独立的变量实例。thread_local 可以和static 与extern关键字联合使用,这将影响变量的链接属性。
thread_local unsigned long hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);
//8this_thread_hierarchy_value①。它被初始化为最大值⑧
- 条件变量
std::mutex con_mtx;
std::deque<int> que;
std::condition_variable con;
void function_1()
{
int count=10;
while (count >0)
{
std::unique_lock<std::mutex> locker(con_mtx);
que.push_front(count);
locker.unlock();
cout<<"push data"<<count<<endl;
con.notify_one();
std::this_thread::sleep_for(chrono::seconds(1));
count--;
}
}
void function_2()
{
int data=0;
while (data != 1)
{
std::unique_lock<std::mutex> locker(con_mtx);
con.wait(locker,[](){return !que.empty();});
//wait 需要解锁 加锁,只能使用unique_lock,lock_guard不能满足wait的底层实现
//第二个参数是为了防止该函数线程被自己激活,即伪激活,队列为空时线程等待,不为空激活;
data=que.back();
que.pop_back();
locker.unlock();
cout<<"get data:"<<data<<endl;
}
}
void test_condi()
{
std::thread t1(function_1);
std::thread t2(function_2);
t1.join();
t2.join();
}
- 异步获取线程返回结果
//std::future 用于访问异步操作的结果
//std::promise std::packaged_task 比future高一层
//packaged_task包装的是一个函数
//获取线程中的某个值使用std::promise
//获取线程函数返回值使用packaged_task
void test_future(std::future<int> &fut)
{
int x = fut.get();
}
void use_futur()
{
std::promise<int> prom;
std::future<int> fut=prom.get_future();
std::thread thread(test_future,std::ref(fut));
prom.set_value(144);
thread.join();
}
int test_packaged_task(int in)
{
return in+1;
}
void use_packaged_task()
{
std::packaged_task<int(int)> task(test_packaged_task);
std::future<int> fut=task.get_future();
std::thread(std::move(task),5).detach();
fut.get();
}
//async
int factorial(int N)
{
int res=1;
for (size_t i = N; i > 1; i--)
{
res*=i;
}
cout<<"res:"<<res<<endl;
return res;
}
void test_future()
{
int x;
//std::lauch::deferred | std::lauch::async 是否创建子线程
//std::lauch::deferred 不创建线程,表示延迟执行任务,调用get wait时才会执行,
//std::lauch::async 创建子线程
std::future<int> fu = std::async(std::launch::deferred | std::launch::async,factorial,4) ;
x=fu.get(); //get只能调用一次
}
- std库的时间函数
//延迟 100 ms
std::this_thread::sleep_for(std::chrono::milliseconds(100));
//get current time
const auto start = std::chrono::high_resolution_clock ::now();
//delay 500ms
std::this_thread::sleep_for(std::chrono::milliseconds(500));
//get current time
const auto end = std::chrono::high_resolution_clock ::now();
//计算时间差
std::chrono::duration<double,std::milli> elapsed = end-start;
//sleep_until适用于需要在未来的某个特定时间点终止线程执行的场景
auto wake_time = std::chrono::system_clock::now() + std::chrono::seconds(5);
std::this_thread::sleep_until(wake_time);
- std::function 与std::bind的使用
void f(int n1,int n2,int n3,int n4,int n5)
{
std::cout<<n1<<std::endl;
std::cout<<n2<<std::endl;
std::cout<<n3<<std::endl;
std::cout<<n4<<std::endl;
std::cout<<n5<<std::endl;
}
int g(int){}
void UserBind()
{
using namespace std::placeholders;
int n=7;
auto f1=std::bind(f,_2,42,_1,std::cref(n),n);
n=10;
f1(1,2,1001); //此时的函数为f(2,42,1,n,7)
//std::bind的解释
// _2为f1传的参数的第二个, _1为f1传的参数的第一个
//f1传的三个参数,由于bind已经绑定好函数的格式,所以1001不会被传入函数,f1的 1 被应用到bind的_1处,即第一个参数实际是第三个参数,第二个参数 2 被应用到bind的_2处
//所以最后的函数f的参数为 (2,42,1,n,7)
}