多執行緒共用變數

  • 6660
  • 0

多執行緒共用變數時,會發生意料外的錯誤。

參考這篇[多個 Thread 共用變數]

http://www.ez2o.com/Blog/Post/csharp-Mulit-Thread-Lock-Variable


在多個執行緒共用變數時,因為該變數同時給多個執行緒使用,

使得可能同時有多個執行緒改同一個變數,

讓變數的行為不如預期。

原程式碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadSumTest
{
    class Program
    {
        static int threadCount = 0;
        static void HelloThread(Object time)
        {
            Console.WriteLine("--START--Now{0},Thread{1}", ((DateTime) time).ToShortTimeString() , Thread.CurrentThread.ManagedThreadId);
            Random ran = new Random();
            Thread.Sleep(ran.Next(0,1000));
            Console.WriteLine("--E N D--Now{0},Thread{1}", ((DateTime)time).ToShortTimeString(), Thread.CurrentThread.ManagedThreadId);
                for (int i = 0; i < 10000; i++)
                {
                    threadCount++;
                } 
        }
        static void Main(string[] args)
        {            
            List<Thread> ThreadList=new List<Thread>();

            for (int i = 0; i < 10; i++)
            {
                ThreadList.Add(new Thread(new ParameterizedThreadStart(HelloThread)));
                ThreadList[i].Start(DateTime.Now);
            }
            foreach (Thread requestThread in ThreadList)
            {
                requestThread.Join();
            }
            Console.WriteLine(threadCount);

        }
 
    }
}

錯誤結果

錯誤行為不是每次都會發生,但是一定會發生,所以一定要處理。

簡單的方式就是用lock語法把共用變數的區段包起來,

透過新增一個物件來當作辨認的鑰匙,來確保該程式區段每一次都只有一個執行緒才處理。

但此方式會讓多執行緒在某一區段變成單執行緒,故lock的區段不可太長,不然就失去多執行緒的意義了。

修改後程式碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadSumTest
{
    class Program
    {
        static int threadCount = 0;
        static object countLock=new object();
        static void HelloThread(Object time)
        {
            Console.WriteLine("--START--Now{0},Thread{1}", ((DateTime) time).ToShortTimeString() , Thread.CurrentThread.ManagedThreadId);
            Random ran = new Random();
            Thread.Sleep(ran.Next(0,1000));
            Console.WriteLine("--E N D--Now{0},Thread{1}", ((DateTime)time).ToShortTimeString(), Thread.CurrentThread.ManagedThreadId);
            lock (countLock)
            {
                for (int i = 0; i < 10000; i++)
                {
                    threadCount++;
                } 
            }
        }
        static void Main(string[] args)
        {            
            List<Thread> ThreadList=new List<Thread>();

            for (int i = 0; i < 10; i++)
            {
                ThreadList.Add(new Thread(new ParameterizedThreadStart(HelloThread)));
                ThreadList[i].Start(DateTime.Now);
            }
            foreach (Thread requestThread in ThreadList)
            {
                requestThread.Join();
            }
            Console.WriteLine(threadCount);

        }
 
    }
}

另跟多執行緒有關之文章

[執行緒的順序啟動 - Thread.Join方法]

https://dotblogs.com.tw/yc421206/2011/01/04/20575

[如何 使用 多執行緒 Thread / 跨執行緒 存取UI]

https://dotblogs.com.tw/yc421206/2009/02/13/7141