2013-10-23 114 views
1

我已经通过了一些线程教程,但很好奇一件事。std ::线程何时执行线程?

std::thread Child([](){ std::cout << "When am I executed?" << std::endl << std::endl; }); 

//Some other code 

Child.join(); 

//I'm guessing now my thread would be running 

当我打电话join()还是它正在创建时的线程,当我打电话加入之间的某个时候运行正在执行的线程?如果在调用join()时执行它,只是为了检查我的理解,它告诉要执行的部分,并且程序在主线程上继续,并且最终子线程在主线程正在使用的同一内存上完成了一些工作?

如果我想为泛型类创建一个包装,我会想要做类似下面的事情,但我似乎无法完全弄清楚。我对内存管理线程感到困惑。

class Sync { 
private: 
    int val; 

public: 
    Sync() : val(0) {} 
    void Inc() {val++} 
    int Value() { return val; } 
}; 

class Async { 
private: 
    Sync Foo; 
    std::mutex mtx; 
    std::vector<std::thread*> Children; 
public: 
    //I would need a new thread each time I called Inc 
    void Inc() { 
     Children.push_back(new std::thread([&]() { 
      mtx.lock(); 
      Foo.Inc(); 
      mtx.unlock(); 
     })); 


    } 
    //But how do I know when it is safe to delete Child? 
    int Value() { 
     for(auto& thds : Children) { 
      thds->join(); 
      delete thds; 
     } 
     Children.clear(); 
     return Foo.Value(); } 
}; 

我正在考虑一个合适的位置可能是在线程函数结束,因为孩子将不再需要,但如果你试图从内部摧毁自身的线程会发生什么?我猜这听起来会像一个坏主意一样。我怎么知道什么时候可以删除我的线程?有更好的方法吗?

修改上面的代码以反映来自下面的建议。

我现在已经意识到教程中关于抛出异常的问题,所以我应该使用互斥锁而不是mtx.lock()。

回答

4

join的目的是从本质上等待线程完成。因此,一旦Child->join();返回,您的线程已完成。当然,你也可以在析构函数中执行Child->join() [或者在某个其他位置,只要确保它在某个点被调用]。

请注意,线程将在实际创建时间和连接结束之间的某个时间点开始运行。没有办法知道什么时候会发生。如果系统上已有很多线程正在运行,则时间将在所有线程之间分配,并且线程可能不会运行几秒钟。另一方面,如果有一个CPU坐在那里“旋转它的手指”,它可能很可能在主线程脱离new std::thread(...)构造之前启动[因为std::thread将不会返回,直到线程被正确创建,并且一些数据正在存储在线程对象中,所以线程可能在您到达Child->join()时完成。如果不知道系统处于何种状态,则无法分辨出这些选项中的哪一个。

+0

然后线程一旦创建就开始运行?另外,我可以在加入呼叫后调用删除。 谢谢 – Chemistpp

+2

是的,我刚刚编辑了答案,以澄清该线程可能会在创建完成之前启动,或者稍后再完成。这完全取决于系统中有多少个CPU /内核以及系统的总体负载是多少。 –

1

您无法知道您的线程将以哪种顺序或哪个内核运行。

一个posix线程基本上被linux视为一个进程与另一个进程共享它的堆。内核将安排它们并决定使用哪个内核。例如,查看生成的(受保护的)程序集,您将看不到任何“多核/多线程编程”,因为这是在内核级完成的。

这就是为什么函数如连接和诸如互斥/信号量之类的工件存在的原因。照顾比赛条件和其他相关的东西。

1

当我调用join()或正在运行 时,线程在创建线程和我何时调用join之间执行?

线程在创建线程之后执行(启动),具体取决于可能需要一些时间的可用资源。 join()方法等待(块),直到线程完成其工作。

一个建议:我不会将该变量的名称命名为Sync对象This,它令人困惑,因为存在this关键字。

在调用join()Async::Inc()之后,您可以安全地删除std::thread对象,此外,您不需要将引用存储为成员变量,它仅在该函数中使用。

您也可以查看<atomic>表头,std::atomic<int>std::atomic_int

4

操作系统很重要,但它相当普遍。线程获得预定执行。当操作系统开始实际运行时,它是完全不可预知的。在具有多核的机器上,“快速”出现的可能性很大,今天普遍可用。

thread :: join()只是确保线程完成执行。你会从来没有实际上写这样的代码,它是完全没有意义的启动一个线程,然后等待它完成。不妨直接执行线程的代码。创建线程不会导致操作系统陷入困境,同样的结果。

+0

我现在可以看到调用join的同一个函数和sync版本是一样的。现在,我必须多考虑一点,我更了解这一点。谢谢 – Chemistpp