假设我有一个包含5个子线程的线程池。他们正在调用一个名为“functionA()”的函数。如何使函数成为线程安全的?
如果同时调用这5个线程,那么它们是否同时执行? 或者他们是否等到当前在函数中工作的线程完成?
提前致谢..
答案 0 :(得分:14)
如果函数不修改非本地内存并且它不调用任何函数,则该函数已经是线程安全的。在这个(微不足道的)情况下,你不需要做任何事情。
您真的想要考虑保护数据,而不是功能。例如,假设该函数修改了非本地数据结构X.提供一个互斥锁来保护X并在每次访问之前将其锁定并在之后解锁。您可能有多个访问X的函数(例如insertX(),deleteX(),...)。只要你保护数据,你就可以了。
答案 1 :(得分:2)
使用互斥锁可以做到这一点。
或者:
mutex_lock(&mutex);
functionA();
mutex_unlock(&mutex);
或在functionA();
内int functionA() {
mutex_lock(&mutex);
// code
mutex_unlock(&mutex);
}
小心第二个解决方案,因为如果函数有其他退出路径(例如中间的返回)和未解锁的互斥锁,则会出现称为死锁的情况。
答案 2 :(得分:2)
每次调用函数时,都需要确保函数运行完全相同。
这通常意味着您需要保护在函数中访问/写入的任何数据。
对于独立的自由函数,通常意味着从函数中删除静态元素。
如果你指的是某个类的成员函数,那么你需要更加小心 - 因为该类通常会独立存储成员函数的数据,这可能会在对该成员的调用之间发生变化。您通常会使用互斥锁执行此操作。
例如:
//Threadsafe because the function do not change each time it is called
double add_one(double d)
{
return d+1;
}
struct A
{
double m_data;
//Not thread safe because if function called simultaneously then m_data could change more than once before it is returned
double
run(double d)
{
m_data += d;
return m_data/2.0;
}
}
这可以通过添加一个互斥锁来解决,该互斥锁要求两个线程不能同时运行run成员。有关一个好的互斥示例,请参阅Boost.Thread。
struct A
{
double m_data;
mutex m_mutex;
//Thread safe because mutex protects m_data from being written by multiple threads at once
double
run(double d)
{
lock(m_mutex); //lock the function, unlock on destruction.
m_data += d;
return m_data/2.0;
}
}
答案 3 :(得分:0)
执行此操作的一种方法是使用互斥锁来确保在任何时候只在函数中执行单个线程。当然,这假设这是您所指的线程安全类型。线程安全可能意味着许多不同的事情。例如,阅读标题为What is this thing you call "thread safe"?
的Eric Lipperts帖子如果同时调用这5个线程,那么它们是同时执行还是等到当前在函数中工作的线程完成?
除非你强加一些同步机制(例如互斥锁),否则线程会同时执行该函数。这是否是您的功能的问题,以及解决方案是什么,取决于该功能的数据访问模式。
答案 4 :(得分:0)
这取决于你想做什么/你在线程安全下的理解。 通常,您不希望线程同时访问相同的数据,因此您将其序列化。 但这意味着如果一个线程在函数中,则所有其他线程都必须等待。这称为互斥(互斥)。 另一种方法是在函数中使用多个线程,但数量有限。 在那里你可以申请一个信号量。
那你想做什么?