c# - 未在多线程程序中读取正确的属性值
问题描述
我遇到了一个场景,我不确定为什么在多个线程中我没有从属性中读取正确的属性值。我编写了一个小型控制台应用程序来说明这种情况
class Program
{
private static Test MyObject;
static void Main(string[] args)
{
MyObject = new Test();
Task.Run(() =>
{
var obj = MyObject as Test;
Console.WriteLine("1: " + obj.MyValue);
Update("Thread1");
Console.WriteLine("2: " + obj.MyValue);
});
Task.Run(() =>
{
var obj = MyObject as Test;
Console.WriteLine("3: " + obj.MyValue);
Thread.Sleep(100);
Update("Thread2");
Console.WriteLine("4: " + obj.MyValue);
});
void Update(string val)
{
lock (MyObject)
{
MyObject.MyValue = val;
}
}
Thread.Sleep(400);
Console.WriteLine("5: " + MyObject.MyValue);
}
}
class Test
{
public string MyValue { get; set; }
}
运行上面的行我得到这个输出
1:
2: Thread1
3:
4: Thread2
5: Thread2
我的期望是“3:”应该总是说“3:Thread1”,因为之前更新了同一个对象。但事实并非如此,我不确定我在这里错过了什么......有人可以对此有所了解吗?
只是一个小提示...如果您运行代码并获得不同的顺序,请重新运行它...我只对上面的顺序感兴趣。
解决方案
有几件事:
- 您的
Update()
方法同步对属性的写入MyValue
,但对读取没有任何作用。因此,运行时可以自由地为对象使用缓存值。这不太可能与您观察到的输出有关,因为实际上您不太可能看到这一点,尤其是在 x86 硬件上。但… - 更重要的是,您的代码所展示的只是输出顺序可能会产生误导。该类
Console
具有同步以确保来自多个线程的一致输出,但代码中没有任何内容可以确保如果以特定顺序编写输出行,则导致这些输出行的代码(例如读取MyValue
属性)发生与显示这些输出行的顺序相同。
换句话说,仅仅因为控制台"2:"
在该行之前显示了该"3:"
行,这实际上并不意味着调用 toUpdate("Thread1")
发生在调用 to 之前Console.WriteLine("3: " + obj.MyValue);
lock
如果您想确保输出行与程序中语句的执行顺序相匹配,您还需要使用语句来保护各个操作。
更具体地说,请考虑以下可能的代码执行顺序:
线程 1 线程 2 -------- -------- “值”参数 <= “3:” + obj.MyValue Console.WriteLine("1:" + obj.MyValue); 更新(“线程1”); Console.WriteLine("2:" + obj.MyValue); Console.WriteLine(值);
Console.WriteLine()
即,第二个线程在第一个线程开始甚至开始其逻辑之前计算传递给的参数是完全合法的。但随后第一个线程也可以在第二个线程有机会实际调用该Console.WriteLine()
方法之前抢占第二个线程。
如果发生这种情况,那么您会看到线程 1 的预期输出顺序,但线程 2 的输出写入了一个明显陈旧的MyValue
属性版本,因为它是在线程 1 运行其任何逻辑之前检索到的。
要解决该特定情况,您可以使用lock
调用WriteLine()
。例如:
lock (MyObject) Console.WriteLine("1: " + obj.MyValue);
请注意,您需要在每次调用Console.WriteLine()
. 这将确保写入控制台的任何值都是调用时属性的最新值。
推荐阅读
- github - 将 Zuul CI 与 GitHub 连接
- python - 当前期货和网络抓取:隐式等待无法正常工作
- amazon-web-services - 为什么即使消耗的 RCU 超过预置的 RCU,dynamodb 也不会受到限制?
- coinbase-api - 我可以使用 coinbase 为将在 Apple Store/Google Play 上的市场应用程序转移稳定币吗?
- .net - 链接已被 CORS 策略阻止:没有“访问控制允许来源”
- c++ - 如何从两个不同的双向链表中更改两个不同的节点?
- javascript - Linux 和 MacOS 上的 Webpack 开发服务器热重载,但 Windows 上没有?
- python - 为什么启动 Numba cuda 内核最多可使用 640 个线程,但在有大量可用 GPU 内存时会因 641 而失败?
- react-native - 如何在从我的反应本机应用程序共享/发布文本到 Facebook 时避免“登录”
- python - 计算熊猫中的数组