執行緒安全 (Thread-safe)

現代程式設計為了提升效能,通常採用多執行緒的方式執行,但若沒有注意執行緒安全,往往會造成意想不到的錯誤。

假設現在有一個程式有 Main & Timer 兩個 Thread 都會透過 i2c command 存取硬體,模擬的程式碼如下:


using System;
using System.Threading;

namespace ThreadTest
{
    class I2cGpu
    {
        public void NormalRead(int index)
        {
            Console.WriteLine("{0} ==> NormalRead{1}", Thread.CurrentThread.Name, index);
            Thread.Sleep(500);
            Console.WriteLine("{0} <== NormalRead{1}", Thread.CurrentThread.Name, index);
        }

        public void NormalWrite(int index)
        {
            Console.WriteLine("{0} ==> NormalWrite{1}", Thread.CurrentThread.Name, index);
            Thread.Sleep(2000);
            Console.WriteLine("{0} <== NormalWrite{1}", Thread.CurrentThread.Name, index);
        }
    }

    class ThreadTest
    {
        static void Main(string[] args)
        {
            Thread threadMain = new Thread(MainThread);
            threadMain.Name = "Main ";

            Thread threadTimer = new Thread(TimerThread);
            threadTimer.Name = "Timer";

            threadMain.Start();
            threadTimer.Start();
        }

        private static void TimerThread()
        {
            I2cGpu i2CGpu = new I2cGpu();

            for (int i = 0; i < 3; i++)
            {
                i2CGpu.NormalRead(i);
                i2CGpu.NormalWrite(i);
            }
        }

        private static void MainThread()
        {
            I2cGpu i2CGpu = new I2cGpu();

            for (int i = 0; i < 3; i++)
            {
                i2CGpu.NormalRead(i);
                i2CGpu.NormalWrite(i);
            }
        }
    }
}

執行結果如下,可以發現兩個 Thread 在做底層 i2c Read / Write 時是交錯的,


此時可利用 lock 來保護 Read / Write 的過程中是不會被其他 Thread 中斷的

using System;
using System.Threading;

namespace ThreadTest
{
    class I2cGpu
    {
        static object locker = new object();

        public void NormalRead(int index)
        {
            lock (locker)
            {
                Console.WriteLine("{0} ==> NormalRead{1}", Thread.CurrentThread.Name, index);
                Thread.Sleep(500);
                Console.WriteLine("{0} <== NormalRead{1}", Thread.CurrentThread.Name, index);
            }
        }

        public void NormalWrite(int index)
        {
            lock (locker)
            {
                Console.WriteLine("{0} ==> NormalWrite{1}", Thread.CurrentThread.Name, index);
                Thread.Sleep(2000);
                Console.WriteLine("{0} <== NormalWrite{1}", Thread.CurrentThread.Name, index);
            }
        }
    }

    class ThreadTest
    {
        static void Main(string[] args)
        {
            Thread threadMain = new Thread(MainThread);
            threadMain.Name = "Main ";

            Thread threadTimer = new Thread(TimerThread);
            threadTimer.Name = "Timer";

            threadMain.Start();
            threadTimer.Start();
        }

        private static void TimerThread()
        {
            I2cGpu i2CGpu = new I2cGpu();

            for (int i = 0; i < 3; i++)
            {
                i2CGpu.NormalRead(i);
                i2CGpu.NormalWrite(i);
            }
        }

        private static void MainThread()
        {
            I2cGpu i2CGpu = new I2cGpu();

            for (int i = 0; i < 3; i++)
            {
                i2CGpu.NormalRead(i);
                i2CGpu.NormalWrite(i);
            }
        }
    }
}

執行結果如下,可以發現不再有交錯的情況了


留言

這個網誌中的熱門文章

Linux 批次檔的寫法

【分享】如何顯示 Debug Message

[分享] Visual Studio 遠端偵錯