thread

  • 线程创建后调用join函数等待其执行结束,否则main线程先退出,导致异常。
  • detach是用来和线程对象分离的,这样线程可以独立地执行。不过这样由于没有thread对象指向该线程而失去了对它的控制,当对象析构时线程会继续在后台执行,但是当主程序退出时并不能保证线程能执行完。如果没有良好的控制机制或者这种后台线程比较重要,最好不用detach而应该使用join。
class Test
{
public:
  void runMultiThread();
private:
  int calculate(int from, int to);
}  

void Test::runMultiThread()
{
    std::thread t1(&Test::calculate, this,  0, 10);     // 调用类成员函数
    std::thread t2(&Test::calculate, this, 11, 20);
    t1.join();
    t2.join();
}

mutex

用来保证线程同步的,防止不同的线程同时操作同一个共享数据。但是使用mutex是不安全的,当一个线程在解锁之前异常退出了,那么其它被阻塞的线程就无法继续下去。

int cnt = 20;
mutex m;
void t1(){
    while (cnt > 0){    
        m.lock();
        if (cnt > 0){
            --cnt;
            cout << cnt << endl;
        }
        m.unlock();
    }
}
void t2(){//与t1一致}
int main()
{
    
    thread th1(t1);
    thread th2(t2);
    
    th1.join();
    th2.join();

    return 0;
}

运行时cnt是依次递减的,不会因为多线程而打乱次序。

lock_guard && unique_lock

  • 使用lock_guardunique_lock则相对安全,它是基于作用域的,能够自解锁,当该对象创建时,它会像m.lock()一样获得互斥锁,当生命周期结束时,它会自动析构unlock,不会因为某个线程异常退出而影响其他线程。
  • lock_guard在创建时加锁,在析构时解锁。unique_lock可以有条件的解锁。
template <typename T>
class ThreadSafeQueue{
public:
         void Insert(T value);
         void Popup(T &value);
         bool Empety();
 
private:
       mutable std::mutex mut_;
       std::queue<T> que_;
       std::condition_variable cond_;
};

template <typename T>
void ThreadSafeQueue::Insert(T value){
    std::lock_guard<std::mutex> lk(mut_);
    que_.push_back(value);
    cond_.notify_one();         // 唤醒其他wait函数
}
 
 
template <typename T>
void ThreadSafeQueue::Popup(T &value){
    std::unique_lock<std::mutex> lk(mut_);
    cond_.wait(lk, [this]{return !que_.empty();});
    value = que_.front();
    que_.pop();
}

条件变量

条件变量是一种同步原语(Synchronization Primitive)用于多线程之间的通信,它可以阻塞一个或同时阻塞多个线程 成员函数

  • notify_one notify_all:类似于发出脉冲信号,如果对wait的调用发生在notify之后是不会被唤醒的
  • wait wait_for wait_until

ShengYg

Step after step the ladder is ascended.


Tags •