我正在学习 TPL 并且有疑问。仅供学习之用,希望大家指导我正确的方向。
我希望一次只有一个线程访问变量 sum
,这样它就不会被覆盖。
我的代码如下。
using System;
using System.Threading.Tasks;
class ThreadTest
{
private Object thisLock = new Object();
static int sum = 0;
public void RunMe()
{
lock (thisLock)
{
sum = sum + 1;
}
}
static void Main()
{
ThreadTest b = new ThreadTest();
Task t1 = new Task(()=>b.RunMe());
Task t2= new Task(() => b.RunMe());
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine(sum.ToString());
Console.ReadLine();
}
}
问题 - 我在这段代码中是否正确?
问题-我可以不带锁地做吗,因为我在某处读到它应该避免,因为它不允许任务相互通信。我已经看到一些异步和等待的例子但是我正在使用 .Net 4.0。
谢谢
最佳答案
Am i right in this code
在实现方面是的,但在理解方面是不可行的,因为您在尝试实现逻辑时混淆了新旧世界,让我尝试详细解释一下。
Task t1 = new Task(()=>b.RunMe());
并不像Thread
API 中的预期那样意味着每个新线程时间Task
API 将调用一个线程池线程,因此有可能是两个 Task 对象t1,t2
,大多数时候在同一个线程上执行一个短时间运行的逻辑并且在尝试更新共享对象
时,永远不会出现需要显式- 防止
Sum object
竞争条件的更好方法是Interlocked.Increment(ref sum)
,这是一种对执行基本操作的线程安全机制原始类型
对于您正在执行的操作,更好的 API 是
Parallel.For
,而不是创建单独的Task
,好处是您可以以最小的努力运行任意数量的此类increment
操作,而不是创建一个单独的任务,它会自动阻塞主线程,因此您的代码应如下所示:using System; using System.Threading.Tasks; class ThreadTest { public static int sum; } static void Main() { Parallel.For(0, 1, i => { // Some thread instrumentation Console.WriteLine("i = {0}, thread = {1}", i, Thread.CurrentThread.ManagedThreadId); Interlocked.Increment(ref ThreadTest.sum); }); Console.WriteLine(ThreadTest.sum.ToString()); Console.ReadLine(); } }
在使用 Thread 检测时,您会发现对于两个循环,
0,1
,managed thread id
可能是相同的,从而避免了需要如前所述,为了线程安全
锁定
的竞争条件
关于c# - 任务并行库 (TPL) 中的线程同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39139151/