关于多线程,你需要了解的一些基础知识2---互斥元处理

247 阅读4分钟

1.互斥锁的实现过程很简单,mutex是一个类,首先我们要先创建出类对象 std::mutex mylock,然后在你需要锁的代码块前后加上mylock.lock()和mylock.unlock() ,就可以实现互斥锁的加锁和解锁了。可以具体实现可以看下面的代码


#include <iostream>
#include <thread>
#include <mutex>
 
void work1(int& sum, std::mutex& mylock) {
	for (int i = 1; i < 5000; i++) {
		mylock.lock();
		sum += i;
		mylock.unlock();
	}
}
 
void work2(int& sum, std::mutex& mylock) {
	for (int i = 5000; i <= 10000; i++) {
		mylock.lock();
		sum += i;
		mylock.unlock();
	}
}
 
int fun() {
	int sum = 0;
	for (int i = 1; i <= 10000; i++) {
		sum += i;
	}
	return sum;
}
 
int main()
{
	std::mutex mylock;
	int ans = 0;
	std::thread t1(work1, std::ref(ans), std::ref(mylock));
	std::thread t2(work2, std::ref(ans), std::ref(mylock));
	t1.join();
	t2.join();
	std::cout << "sum1 : " << ans << std::endl;
	std::cout << "sum2 : " << fun() << std::endl;
	return 0;
}

2.用lock_guard类模板

#include <iostream>
#include <thread>
#include <mutex>
 
void work1(int& sum, std::mutex& mylock) {
	for (int i = 1; i < 5000; i++) {
		std::lock_guard<std::mutex> mylock_guard(mylock);
		sum += i;
	}
}
 
void work2(int& sum, std::mutex& mylock) {
	for (int i = 5000; i <= 10000; i++) {
		std::lock_guard<std::mutex> mylock_guard(mylock);
		sum += i;
	}
}
 
int fun() {
	int sum = 0;
	for (int i = 1; i <= 10000; i++) {
		sum += i;
	}
	return sum;
}
 
int main()
{
	std::mutex mylock;
	int ans = 0;
	std::thread t1(work1, std::ref(ans), std::ref(mylock));
	std::thread t2(work2, std::ref(ans), std::ref(mylock));
	t1.join();
	t2.join();
	std::cout << "sum1 : " << ans << std::endl;
	std::cout << "sum2 : " << fun() << std::endl;
	return 0;
}

可以通过大括号来规定实现的范围。比如下面这样

 {
 	std::lock_guard<std::mutex> mylockguard(mylock);
	/*...
	   中间用来写需要加锁的内容
	*/
  }

3.c++线程安全的栈

/*
 * threadsafe_stack.cpp
 *
 *  Created on: Nov 30, 2017
 *      Author: clh01s@163.com
 *      线程安全的栈
 */

#include <exception>
#include <iostream>
#include <mutex>
#include <stack>
#include <memory>
#include <string>

using namespace std;

struct empty_stack: exception
{
    const char* what() const throw();
};

template<typename T>
class threadsafe_stack
{
public:
    threadsafe_stack(){}
    threadsafe_stack(const threadsafe_stack& other)
    {
        lock_guard<mutex> lock(other.m);
        data = other.data;
    }
    //禁止使用=
    threadsafe_stack& operator=(const threadsafe_stack&) = delete;

    void push(T new_value)
    {
        //调用锁
        lock_guard<mutex> lock(m);
        /* 调用data.push()可能会抛出异常
         * 如果复制或者移动数据项抛出异常或
         * 者不能分配足够的内存来扩展下层的
         * 数据结构。
         * 不管怎样,stack保证了它是安全的
         * 因此这不是一个问题。
         */
        cout<<"push value = "<<new_value<<endl;
        data.push(move(new_value));
    }

    shared_ptr<T> pop()
    {
        lock_guard<mutex> lock(m);
        /* 在函数pop()重载的第一种形式中
         * 他的代码可能会抛出一个empty_stack
         * 异常,但是没有做任何修改,因此它时安全的
         */
        if(data.empty()) throw empty_stack();
        /* 创建res可能会抛出异常
         * 1.调用make_shared可能会抛出异常
         * 因为它无法为新对象和需要引用计数的
         * 内部数据分配内存。
         * 2.复制构造函数或移动构造函数中返回
         * 数据项被复制/移动到新分配的内存时也
         * 可能会抛出异常。
         * 这两种情况下c++运行库和标准库确保没
         * 有内存泄露并且新对象被正确销毁。因为
         * 你仍然没有修改下层的栈所以没有问题。
         */
        shared_ptr<T> const res(
                make_shared<T>(move(data.top())));
        //调用data.pop()保证了不会抛出异常,因此pop()的重载是异常安全的
        data.pop();
        return res;
    }

    void pop(T& value)
    {
        lock_guard<mutex> lock(m);
        if(data.empty()) throw empty_stack();
        /* 这次于第一次时类似的,只不过这次是拷贝赋值
         * 或移动赋值操作符抛出异常,而不是构造新对象
         * 和一个shared_ptr实例抛出异常
         */
        value = move(data.pop());
        //这于上一个重载相同不会抛出异常
        data.pop();
    }

    bool empty() const
    {
        lock_guard<mutex> lock(m);
        return data.empty();
    }
private:
    stack<T> data;
    mutable mutex m;
};

int main()
{
    threadsafe_stack<string> a;
    a.push("aa");
    a.push("bb");
    return 0;
}

4.使用层次锁来避免死锁

#include "iostream"
#include "stdexcept"
#include "thread"
#include "mutex"
#include "climits"
#include "thread_guard.h"
using namespace std;
 
class hierarchical_mutex {
private:
	std::mutex internal_mutex;
	std::mutex data_mutex;
	unsigned long const hierarchy_value;
	// 保存上一次的层次值,该锁不能重入,所以只需要一个简单数据类型来保存
	unsigned long previous_hierarchy_value;
	// 当前层次值
	static thread_local unsigned long this_thread_hierarchy_value;
	void check_for_hierarchy_violation() 
	{
		// 当前线程锁定了更低等级的或者是同等级的锁
		if (this_thread_hierarchy_value <= hierarchy_value)
		{
			throw std::logic_error("mutex hierarchy violated");
		}
	}
	void update_hierarchy_value() 
	{
		// 保存当前层次值
		previous_hierarchy_value = this_thread_hierarchy_value; 
		// 改变当前层次值
		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_violation();
		internal_mutex.lock();
		lock_guard<std::mutex> lock(data_mutex);
		update_hierarchy_value();
	}
	void unlock()
	{
		internal_mutex.unlock();
		lock_guard<std::mutex> lock(data_mutex);
		this_thread_hierarchy_value = previous_hierarchy_value;
		
	}
	bool try_lock() 
	{
		check_for_hierarchy_violation();
		if (!internal_mutex.try_lock())
			return false;
		lock_guard<std::mutex> lock(data_mutex);
		update_hierarchy_value();
		return true; 
	}
};
thread_local unsigned long hierarchical_mutex::this_thread_hierarchy_value = ULONG_MAX;
 
struct Data {
	hierarchical_mutex m;
	int data; 
	Data(unsigned long hierarchical, int data) 
		:m(hierarchical), data(data) {}
	void swap(Data & d) 
	{
		lock_guard<hierarchical_mutex> lock1(m);
		std::chrono::seconds dura(1);
		std::this_thread::sleep_for(dura);
		lock_guard<hierarchical_mutex> lock2(d.m);
		std::swap(d.data, data);
	}
};
 
int main()
{
	Data d1(10000,1), d2(1000,2);
	{
		thread_guard t1(thread([&] {
			try
			{
				d1.swap(d2);
			}
			catch (const std::exception&)
			{
				cout << "operation not be permitted!" << endl;
			}
			
		}));
		thread_guard t2(thread([&] {
			try
			{
				d2.swap(d1);
			}
			catch (const std::exception&)
			{
				cout << "operation not be permitted!" << endl;
			}
		}));
	}
	cout << d1.data << endl;
	system("pause");
	return 0;
}