跟进问题: This question
如链接问题中所述,我们有一个API,它使用事件外观轮询select()来处理用户定义的回调。
我有一个类使用它的类:
class example{
public:
example(){
Timer* theTimer1 = Timer::Event::create(timeInterval,&example::FunctionName);
Timer* theTimer2 = Timer::Event::create(timeInterval,&example::FunctionName);
start();
cout<<pthread_self()<<endl;
}
private:
void start(){
while(true){
if(condition)
FunctionName();
sleep(1);
}
}
void FunctionName(){
cout<<pthread_self()<<endl;
//Do stuff
}
};
这背后的想法是,如果条件为真或计时器启动,您希望同时调用FunctionName。不是一个复杂的概念。我想知道的是,如果在start()函数和回调函数中同时调用FunctionName?这可能会导致我的内存损坏,因为它们访问非线程安全的共享内存。
我的测试告诉我他们确实在不同的线程中运行(仅在我使用事件时才会损坏),即使:cout<<pthread_self()<<endl;
表示它们具有相同的线程ID。
有人可以向我解释这些回调是如何分开的吗?他们得到了什么样的顺序?他们运行什么线程?我假设它们在执行select()的线程中运行,但是什么时候它们获得相同的线程ID?
答案 0 :(得分:3)
真正的答案取决于Timer的实现,但如果您从同一个线程运行回调,则最有可能使用signals或posix timers。无论哪种方式,都不会涉及select()。
使用信号和posix定时器,你几乎无法从信号处理程序安全地做到。只允许使用某些特定的信号安全呼叫,例如read()和write()( NOT fread()和fwrite(),甚至是new和cout)。通常,将执行的是write()到pipe或eventfd,然后在另一个线程中,或运行select()的主事件循环,请注意此通知并处理它。这使您可以安全地处理信号。
答案 1 :(得分:1)
您编写的代码将无法编译,更不用说运行了。示例:: FunctionName需要是静态的,并且需要将对象引用用作回调函数。
如果定时器在不同的线程中运行,则可以通过三个不同的线程调用此函数。