C++11新特性学习--第一篇线程同步死锁及异步线程问题

头文件:#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)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值