c# - 静态变量的并发增量 g = g + 1 看起来是原子的,但不应该是?
问题描述
找不到为什么下面带有注释的代码可以Monitor
在写入时同步g
并始终返回g=50
。我期待一些差异小于50
.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace _7_monitor
{
class Program
{
static object sync = new object();
static int g = 0;
static Barrier b = new Barrier(6, (b) => { Console.WriteLine("barier reached"); } );
public static void tjob(object obj)
{
int t = (int)obj;
for (int i = 0; i < 10; i++)
{
//Monitor.Enter(sync);
g = g + 1;
//Monitor.Exit(sync);
Console.WriteLine("thr {0} iter={1}", t , i);
}
b.SignalAndWait();
}
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
Thread d = new Thread(tjob);
d.Start(i);
}
Console.WriteLine("waiting");
b.SignalAndWait();
Console.WriteLine("g={0}",g);
Console.ReadLine();
}
}
}
解决方案
正如其他人所指出的那样,一个 10 的循环将完成得如此之快,以至于其他线程很可能还没有开始,所以g
无论如何您都可能顺序访问全局静态变量,因此显然观察到一致的结果。
使用更长的循环(我已经去掉了一些绒毛)而周围没有保护g
,我们确实得到了类似于随机数生成器的东西。
var threads = new List<Thread>();
for (var i = 0; i < 5; i++)
{
var d = new Thread(x => {
for (var loop = 0; loop < 100000; loop++)
{
// unsynchronized mutation
g = g + 1;
}
});
d.Start(i);
threads.Add(d);
}
foreach (var t in threads)
{
t.Join();
}
Console.WriteLine("g={0}", g); // 158609, 173331, 127983, ... (i7 with 4 HT Cores)
根据@Jurgis 的评论,已将 Interlocked.Increment 而不是or提供Monitor
给.Netlock
替换g = g + 1
为Interlocked.Increment(ref g);
返回预期:
g=500000
(显然,在现实世界中,并行化只会争夺共享变量的工作绝对没有意义)
推荐阅读
- scala - spark分区中的数据是什么时候真正实现的?
- amazon-web-services - 模板包含错误。:[/Resources/CloudTrail/Type/EventSelectors] AWS CloudFormation CloudTrail 中的模板中不允许使用“null”值
- firebase - 如何使用多个文档计算 Firestore 中的交易限额?
- c# - 使用带有表达式树的 c#.NET 创建 RPN 计算器
- android - 是否可以将针对 Android 设备的应用程序发布到 Google Play 商店并返回 KitKat?
- pandas - 如何在同一列中找到具有不同变量的均值和标准差
- ios - 无法使用 Appcelerator 构建到 iOS 的临时分发
- laravel - Vue / Laravel - 动态多表单保存数据
- python - 如何解决泡菜编码?
- python - 如何在python中将firebase firestore geopoint转换为latlng