【发布时间】:2019-02-20 12:54:31
【问题描述】:
让我们考虑两个或更多线程和一个资源。如果这是相关的,我在 Ubuntu 上使用 C++11。 下面的代码说明了这种情况:
#include <thread>
#include <mutex>
class Res
{
//Data
};
void use_resource(const Res& rsc) {/*Do stuff*/}
void modify_resource(Res& rsc) {/*Modify the resource*/}
class A
{
Res resource;
std::mutex resource_mtx;
std::thread thd;
public:
A()
{
thd = std::thread(&A::loop,this);
}
void loop()
{
while(true)
{
use_resource(resource); //(Case 1)
//Some work
{
std::lock_guard<std::mutex> mlock(resource_mtx);
modify_resource(resource); //(Case 2)
}
}
}
Res get_resource()
{
std::lock_guard<std::mutex> mlock(resource_mtx);
return resource; //(Case 3)
}
};
int main()
{
A a;
while(true)
{
Res res1 = a.get_resource();
//Do stuff with the resource
}
}
我们有一个包含一些数据的资源。 A 中的函数 loop() 仅在第一个线程上运行。其他线程可以调用 get_resource() 来访问资源。资源只能通过modified_resource函数修改。
我的理解是在情况 2 和 3 中需要加锁,因为情况 2 涉及写入,情况 3 涉及从另一个线程读取。
我想知道的是在情况 1 中是否需要锁。从cppreference.com 来看,数据竞争的定义似乎是:
当一个表达式的求值写入一个内存位置并且 另一个评估读取或修改相同的内存位置, 表达式被称为冲突。有两个冲突的程序 评估存在数据竞争,除非:
i) 两个评估在同一个线程或同一个信号处理程序中执行,或者
ii) 两个冲突的求值都是原子操作(见 std::atomic),或
iii)其中一个冲突的评估发生在另一个之前(参见 std::memory_order)
根据这些定义,我认为我的代码中的案例 1 没有数据竞争:
1) 和 2) 之间没有冲突(它们在同一个线程上,i) 适用)。
1) 和 3) 之间没有冲突(都不是写)。
这引出了我的两个问题:
Q1) 在提供的具体代码中,是否需要通过锁定 resource_mtx 来保护对 use_resource 的调用(案例 1)以避免数据竞争?
Q2) 如果这次案例 1 和 2 可以在 loop() 函数中以任何顺序或数量重复,同样的问题?一个任意的例子是:
while(true)
{
{
std::lock_guard<std::mutex> mlock(resource_mtx);
modify_resource(resource); //(Case 2)
}
use_resource(resource); //(Case 1)
{
std::lock_guard<std::mutex> mlock(resource_mtx);
modify_resource(resource); //(Case 2)
}
use_resource(resource); //(Case 1)
use_resource(resource); //(Case 1)
{
std::lock_guard<std::mutex> mlock(resource_mtx);
modify_resource(resource); //(Case 2)
}
}
如上所述,我的猜测是在这两种情况下都不需要锁,但我很可能会遗漏一些东西,比如编译器重新排序,甚至是数据竞争的实际定义。
到目前为止,我只看到有关来自单独线程的只读访问的问题,这是我的情况 2(there 和 there)或两个不同线程之间的交互(there),但我有在同一个线程中没有看到这个只读访问的具体问题。
编辑:编辑代码以实际反映多线程场景。
【问题讨论】:
-
当您不执行多线程时,您的程序在单线程中运行。也许这已经回答了你的问题......
-
一个线程一次只能做一件事。它不能同时读取和写入变量。
-
也许我的例子当时还不清楚,对此感到抱歉。虽然循环确实在单个线程上运行,但还有其他线程可以访问在单独的线程上运行的 A 类。那些其他线程可以在循环运行时调用 get_resource。
-
顺便说一句,我并不想刻薄。不管我的评论如何,我认为这是一个完全有效的问题。正确同步数据绝非易事。我不得不承认,我没有时间详细阅读您的代码。如果你能把它变成minimal reproducible example
-
你的标题也让我失望了。在你的情况是你需要保护变量,因为它可以被不同的线程同时访问。
标签: c++ multithreading