我的代码中有一个错误,我发现这是一个竞争条件。我知道这是一种竞争条件,因为它会间歇性地发生。我研究了如何防止这些竞争条件,我遇到了这个
for ( int i = 0; i < 10000000; i++ )
{
//lock x
x = x + 1;
//unlock x
}
有人可以详细说明我如何实现这些锁吗?
答案 0 :(得分:4)
您的示例建议,您在线程中要执行的操作是对int变量的操作。如果确实如此,正如其他人所指出的那样,std::atomic
可能是实现它的最简单方法。
#include <thread>
#include <atomic>
#include <iostream>
std::atomic<int> x = 0;
void increment()
{
for(int i = 0; i < 10000000; ++i)
{
++x;
}
}
int main()
{
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << x;
}
但是,为了保护更复杂的操作不会同时在多个线程中执行,您应该使用std::lock_guard
。它使用RAII(资源获取是初始化)原则来锁定互斥锁的生命周期。
#include <thread>
#include <mutex>
#include <iostream>
int x = 0;
std::mutex mtx;
void increment()
{
for ( int i = 0; i < 10000000; i++ )
{
std::lock_guard<std::mutex> lock(mtx); //lock mtx
++x;
// mtx is automatically released when lock
// goes out of scope -> RAII
}
}
int main()
{
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << x;
}
修改强>
根据您的评论,这是另一个例子:
#include <thread>
#include <mutex>
#include <iostream>
class Foo
{
public:
void increment()
{
for ( int i = 0; i < 10000000; ++i )
{
std::lock_guard<std::mutex> lock(mtx); //lock mtx
++x;
// mtx is automatically released when lock
// goes out of scope -> RAII
}
}
void decrement()
{
for ( int i = 0; i < 10000000; ++i )
{
std::lock_guard<std::mutex> lock(mtx); //lock mtx
--x;
// mtx is automatically released when lock
// goes out of scope -> RAII
}
}
static int x;
static std::mutex mtx;
};
int Foo::x = 0;
std::mutex Foo::mtx;
int main()
{
std::thread t1(&Foo::increment, Foo());
std::thread t2(&Foo::decrement, Foo());
t1.join();
t2.join();
std::cout << Foo::x;
}
答案 1 :(得分:2)
我知道这是一种竞争条件,因为它会间歇性地发生
虽然竞赛条件通常会间歇性地发生,但还有其他类型的错误具有相似的行为,因此您的推理并不能保证准确。也就是说,在没有看到程序的情况下,确定在具有多个线程的程序中这是一个非常可能的问题。
有人可以详细说明我如何实现这些锁吗?
互斥锁无法在C ++中实现。它们通常使用机器指令Test-and-set实现。
但是,您自己并不需要实施锁定。从C ++ 11开始,标准库已经包含了一个实现。更好的是,它包含更高级别的原子类型(std::atomic
),它们提供原子访问而不显式锁定执行(原子类型的实现甚至可以使用更有效的指令来避免锁定,具体取决于目标架构)。 p>
如果您的标准库已过时,则可以使用操作系统提供的线程API。它几乎肯定会为您提供某种互斥结构。