程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C# 線程會合實例,

C# 線程會合實例,

編輯:C#入門知識

C# 線程會合實例,


有這樣一個題目:四個線程t1,t2,t3,t4,向4個文件中寫入數據,要求:t1只能寫入“1”,t2只能寫入“2”,t3只能寫入“3”,t4只能寫入“4”,對4個文件A,B,C,D寫入如下內容:

  • A:123412341234.....
  • B:234123412341....
  • C:341234123412....
  • D:412341234123....

簡單分析一下,對於A文件,t1寫入“1”後,我們希望通知t2來寫“2”,並且t1前往D文件等著去寫“1”,以此類推。

1. 通過等待句柄實現

顯然可以用等待句柄來實現,通知t2來寫“2”相當於在一個等待句柄上調用 Set 方法,等待在D文件上寫“1”相當於在另一等待句柄上調用了 WaitOne 方法,下邊是利用4個 AutoResetEvent 來實現它:

class Program
{
    private static List<StreamWriter> _sws;

    private static List<AutoResetEvent> _whs;

private static void Main(string[] args) { var fileNames = new List<string> {"A", "B", "C", "D"}; // 創建或清空文件 fileNames.ForEach(name => { if (!File.Exists(name)) File.Create(name); else { using (var sw = new StreamWriter(name)) sw.Write(""); } }); _sws = fileNames.Select(File.AppendText).ToList(); // 為每個文件寫入建立一個等待句柄 _whs = fileNames.Select(name => new AutoResetEvent(false)).ToList(); // 創建並啟4個線程執行任務 var threads = new List<Thread> { new Thread(() => Work(1)), new Thread(() => Work(2)), new Thread(() => Work(3)), new Thread(() => Work(4)) }; threads.ForEach(t => t.Start()); // 等待線程結束並關閉 StreamWrite threads.ForEach(t => t.Join()); Console.WriteLine("任務完成!"); _sws.ForEach(sw => sw.Close()); } static void Work(int threadIndex) { var next = threadIndex - 1; // 為讓程序能結束,就打印100次 for (int i = 0; i < 100; i++) { var wh = _whs[next]; var sw = _sws[next]; lock (sw) { sw.Write(threadIndex); } next = (next - 1) < 0 ? 3 : next - 1; WaitHandle.SignalAndWait(wh, _whs[next]); //在wh上發信號,並在下一個等待句柄上等待執行 } } }

上述例子中我們創建了4個線程來分別打印1,2,3,4,並且為每個文件的寫入創建了4個等待句柄來進行信號通信。最後主線程在等待所有線程結束後,關閉文件流。為讓程序能正常結束,在 Work 方法中就只循環寫100次。

以t1(列表中第一個線程)為例,在A文件中打印1後,調用 WaitHandle.SignalAndWait(wh,wh[next]),即在wh上發信號通知可以接著寫入了,並在下一個等待句柄上等待寫入信號。

關於 SinalAndWait 的可以參見 Thread in C# 或者對應的 中文翻譯 。

2. 通過 Barrier 類實現

除了通過等待句柄可以實現題目要求外,同樣可以通過 Wait 和 Pulse 來實現。如果是FrameWork 4.0或更高的版本,可以通過 Barrier 類(它是建立在 Wait / Pulse 和自旋鎖基礎上的)更簡單的實現這個題目。

class Program
{
    private static Barrier _barrier;

    private static void Main(string[] args)
    {
        var fileNames = new List<string> { "A", "B", "C", "D" };

        // 創建或清空文件
        fileNames.ForEach(name =>
        {
            if (!File.Exists(name))
                File.Create(name);
            else
            {
                using (var sw = new StreamWriter(name))
                    sw.Write("");
            }
        });
        
        // 在_barrier上調用SignalAndWait的線程會被阻塞直到這個方法被調用4次
        _barrier = new Barrier(4);

        _sws = fileNames.Select(File.AppendText).ToList();

        // 創建並啟4個線程執行任務
        var threads = new List<Thread> {
            new Thread(() => Work(1)), new Thread(() => Work(2)), new Thread(() => Work(3)), new Thread(() => Work(4))
        };
        threads.ForEach(t => t.Start());

        // 等待線程結束並關閉 StreamWrite
        threads.ForEach(t => t.Join());
        Console.WriteLine("任務完成!");
        _sws.ForEach(sw => sw.Close());
    }

    static void Work(int threadIndex)
    {
        var next = threadIndex - 1;
        for (int i = 0; i < 100; i++)
        {
            var sw = _sws[next];
            lock (sw)
            {
                sw.Write(threadIndex);
            }
            _barrier.SignalAndWait();
            next = (next - 1) < 0 ? 3 : next - 1;
        }
    }
}

使用了一個 Barrier 類來替代4個等待句柄,線程調用 SignalAndWait 後會阻塞,直到這個方法被調用4次。在這個例子中意味著4個線程總是在同步進行著打印,下圖可以很好的解釋 Barrier 類:

 關於 Barrier 類,可以參見 Thread in C# 或者對應的 中文翻譯 。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved