C++11 多线程线程共享数据

版权声明:所有的博客都是博主的个人笔记。。。。。 https://blog.csdn.net/qq_35976351/article/details/84062558

共享数据的问题

这些在操作系统中都有详细的介绍,可以回顾操作系统课程。。很典型的就是数据竞争问题。

互斥量保护数据

最原始的方式:使用std::mutex创建互斥量,使用成员lock()加锁,使用成员unlock()解锁。但是这种方式需要我们在每个函数出口都调用一次unlock(),过于繁琐。。。
实例:

// std::lock example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex, std::lock

std::mutex foo,bar;       // 全局锁

void task_a () {
  // foo.lock(); bar.lock(); // replaced by:
  std::lock (foo,bar);
  std::cout << "task a\n";
  foo.unlock();
  bar.unlock();
}

void task_b () {
  // bar.lock(); foo.lock(); // replaced by:
  std::lock (bar,foo);
  std::cout << "task b\n";
  bar.unlock();
  foo.unlock();
}

int main ()
{
  std::thread th1 (task_a);
  std::thread th2 (task_b);

  th1.join();
  th2.join();

  return 0;
}
/*
输出结果:
task a
task b
*/

一般使用std::lock_guard模板类,在构造的时候对互斥量加锁,析构时解锁。

#include <iostream>
#include <mutex>
#include <thread>
#include <stdexcept>

std::mutex mtx;

void print_even(int x) {
    if(x % 2 == 0)
        std::cout << x << " is even\n";
    else
        throw (std::logic_error("not even"));
}

void print_thread_id(int id) {
    try {
        std::lock_guard<std::mutex>lck(mtx);  // 函数结束时,自动析构解锁
        print_even(id);  // 相当于在这里加锁了
    } catch(std::logic_error&) {
        std::cout << "[exception caught]\n";
    }
}

int main() {
    std::thread threads[10];
    for(int i = 0; i < 10; ++i) {
        threads[i] = std::thread(print_thread_id, i + 1);
    }
    for(auto& th : threads)
        th.join();
}

预防死锁

一般都是编程时候进行考虑的。。。方法留作后续补充。。操作系统上的思想为指导。

注意合理组织代码

C++中,如果是直接对地址进行操作,那么锁无法阻止这种行为。

class some_data
{
  int a;
  std::string b;
public:
  void do_something();
};

class data_wrapper
{
private:
  some_data data;
  std::mutex m;
public:
  template<typename Function>
  void process_data(Function func)
  {
    std::lock_guard<std::mutex> l(m);
    func(data);    // 1 传递“保护”数据给用户函数
  }
};

some_data* unprotected;

void malicious_function(some_data& protected_data)
{
  unprotected=&protected_data;  // 这里直接获取地址,那么unprotected的操作无法被锁限制
}

data_wrapper x;
void foo()
{
  x.process_data(malicious_function);    // 2 传递一个恶意函数
  unprotected->do_something();    // 3 在无保护的情况下访问保护数据
}

因此代码中尽量避免直接对临界区的数据进行地址操作。

猜你喜欢

转载自blog.csdn.net/qq_35976351/article/details/84062558
今日推荐