简单的 C# 并发/多线程
本文关键字:多线程 并发 简单 | 更新日期: 2023-09-27 18:28:50
我可能在这里缺少一些基本的东西,但仍然感谢您在理解这一点方面的帮助。所以我有以下简单的多线程程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
// List<int> outcome = new List<int>();
Test t = new Test();
Thread thread1 = new Thread(new ThreadStart(t.call1));
Thread thread2 = new Thread(new ThreadStart(t.call2));
thread1.Start();
thread2.Start();
Thread.Sleep(3000); //Give enough time for threads to end
Console.Write("{0},", t.mSum);
t.mSum = 0;
}
}
class Test
{
public int mSum = 0;
public void call1()
{
//lock (this)
//{
for (int i = 0; i < 100; i++)
{
Console.WriteLine("Hello Thread 1, mSum value: {0}", mSum);
mSum = mSum + 1;
Console.WriteLine("Goodbye Thread 1, mSum value: {0}", mSum);
}
//}
// Console.WriteLine(mSum);
}
public void call2()
{
for (int i = 0; i < 100 ; i++)
{
Console.WriteLine("Hello Thread 2, mSum value: {0}",mSum);
mSum = mSum + 1;
Console.WriteLine("Goodbye Thread 2, mSum value: {0}",mSum);
}
}
}
}
所以我希望这个输出是不确定的,因为上下文切换可以随时发生,对吗?但是当我运行该程序时,我得到以下输出(只有输出的一部分,由于我 stackoverflow.com 问题发布技能不佳而格式不正确(:
你好线程 1,mSum 值:62 再见线程 1,mSum 值:63 你好线程 1,mSum 值:63 再见线程 1,mSum 值:64 你好线程 2,mSum 值:59 再见线程 2,mSum 值:65 你好线程 2,mSum 值:65 再见线程 2,mSum 值:66
所以,假设我写对了,并且 mSum 确实在线程之间共享(看起来像...... - 我该如何解释第 3 行?线程 2 读取 59,加上 1,然后我们得到 65!
我发现了一种新的数学吗?:)
你没有锁定共享变量mSum
,mSum = mSum + 1
也不是原子操作。很明显,打印到控制台,递增变量,然后再次打印到控制台并不是原子的,更:)线程交错的方式有很多种。例如:
1( mSum = 0 [线程 1 正在工作]
2( mSum = 1 [线程 1 正在工作]
3( mSum = 2 [线程 2 正在工作]
4( ...
5( mSum = 59 [线程 2 正在工作],并且在"你好..."之后被抢占
6( mSum = 60 [线程 1 正在工作]
7( mSum = 61 [线程 1 正在工作]
8( ...
9( mSum = 64 [线程 2 正在工作] 在递增线之前唤醒 线程 2 继续,并计算 65
在5)
Thread2
即使在mSum = mSum + 1
从内存中读取 mSum 之后但在计算mSum + 1
之前,也可能被抢占。
由于您使用的是多个线程,因此该值很可能在第一次和第二次Console.WriteLine
调用之间发生了更改。
如果要确保报告添加将使用的正确值,则必须使用锁。
mSum 在第一个 WriteLine 和第二个 WriteLine 之间同时从 2 个线程 A 和 B 更改 mSum 可能会更改。 mSum 没有锁定或易失性,因此您在内存中没有任何障碍,因此您可能会成为一个非常奇怪的结果,但这取决于您的 CPU 类型和现金。
只需将"易失性"一词放在 mSum 之前,这意味着 mSum 将不会在 CPU 中兑现。再次运行应用程序并查看控制台!?
正如您所指出的,mSum
在多个线程之间共享,则分辨率要么是锁,要么mSum
转换为volatile
整数:
http://msdn.microsoft.com/en-us/library/x13ttww7.aspx
所以你可以改变这个:
public int mSum = 0;
自:
public volatile int mSum = 0;
但正如其他人指出的那样,在更复杂的情况下,您可能希望使用锁。
此外,无需将您的调用包装在 ThreadStart
的新实例中,因此取而代之的是:
new Thread(new ThreadStart(t.call1));
你可以这样做:
new Thread(t.call1);