C++: Std::Thread Join時出現Resource deadlock avoided 問題分析

Std::Thread join時出現Resource deadlock avoided 問題分析

1.異常現象

當使用std::thread對象的join函數時, C++程序拋出異常

terminate called after throwing an instance of 'std::system_error'
  what():  Resource deadlock avoided

可能出現上述問題的情況:

  1. 持有線程t的對象(std::thread)在t中調用join函數, 也就是自己join自己.
  2. 兩個或多個線程互相join

2.異常代碼

代碼描述:

  • 定義一個全局變量node, node中包含一個線程對象, 此對象在主線程中被賦予一個線程
  • 當所有線程結束時釋放全局變量node時, 由於node調用了線程對象的join方法, 拋出上述異常.

#include <iostream>
#include <unistd.h>
#include <thread>
using namespace std;
#define PRINT_THREAD_ID(domain) std::cout << domain << " thread id is:" << std::this_thread::get_id() << std::endl;
class Node
{
public:
  Node(){}
  ~Node(){
    if(proc_thread.joinable())
      proc_thread.join();
  }
  void Start(){
    proc_thread = std::thread(&Node::Proc, this);
  }
private:
  void Proc(){
    for(int i = 0; i < 5; i++){
      sleep(1);
    }
  }
  std::thread proc_thread;
};

Node node;

int main(int argc, char **argv)
{
  std::thread t([](){
    int pid = fork();
    if(pid == 0)
      node.Start();
  });
  if(t.joinable())
    t.join();
  return 0;
}

3.異常原因分析

  • 1.node爲全局變量, fork之後被子進程所繼承
  • 2.fork之後, 子進程中的主線程開啓了Node中的一個工作線程, 但是主線程先退出工作線程變成了主線程.
  • 3.當新的工作線程中的函數執行完之後, 函數棧退出, 由於是最後一個線程, 開始回收內存資源準備退出,同時也退出進程.
  • 4.回收資源時,調用了持有此線程的線程對象的Join方法, 所以拋出了異常what(): Resource deadlock avoided, 也就是自己Join自己引起的異常.

4.注意點

  • 1.如果不開啓線程t再fork是不會拋出此異常的, 因爲子進程會繼承運行main函數的主線程, 而Node則是在此線程上被創建的(存儲在靜態存儲區), 當main退出時, 此線程不會立刻退出, 回退到入口函數, 依次釋放棧上的資源,調用join方法.
  • 2.主進程中開啓一個線程t, 線程t中fork一個子進程.在線程中fork的目的是使子進程不繼承主進程的主線程(運行main函數的線程)
  • 3.子進程繼承了主進程中的線程t, 線程t此時爲子進程主線程, Node就是存儲在靜態存儲區的資源, 線程t結束之後不會釋放此資源, 因爲Node不是他的線程棧所創建的.
  • 4.線程中運行的代碼段不單單是傳入的函數, 也包括創建線程和指定函數運行完畢後的處理代碼

5.疑問

衆所周知, 靜態變量和全局變量都是存儲在靜態存儲區的. 而調用main函數的入口函數在一開始的時候會初始化靜態變量和全局變量(不包括靜態局部變量), 那麼創建的這些變量究竟是存在於主線程(調用入口函數的線程)的棧上, 還是存在於靜態存儲區呢?如果有知道的朋友, 麻煩留言告知.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章