简单的 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!

我发现了一种新的数学吗?:)

简单的 C# 并发/多线程

你没有锁定共享变量mSummSum = 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);