當然可以。以下是使用.NET 7對整篇文章進行的翻譯和改寫,包括文章內所有的程式範例:


title: “深入探討 C# 的 Parallel、Task 和多線程編程” keywords:

  • C#
  • Parallel
  • Task
  • 多線程
  • 異步程式
  • 高效能程式 linkTitle: “CSharpParallelTaskMultithreading” date: 2023-12-14 draft: true description: > 本文深入探討 C# 中的 Parallel、Task 和多線程編程概念、使用方法及其在異步程式和多線程應用中的最佳實踐。 tags: [“CSharp”, “Parallel”, “Task”, “多線程”, “異步程式”, “高效能程式”] category: [“CSharp”] images:
    • /images/CSharpParallelTaskMultithreading.png

本章將介紹多線程環境中操作共享資源的常見技巧。您將學習到:

  • 執行基本原子操作
  • 使用 Mutex 構造
  • 使用 SemaphoreSlim 構造
  • 使用 AutoResetEvent 和 ManualResetEventSlim 構造
  • 使用 CountDownEvent 和 Barrier 構造
  • 使用 ReaderWriterLockSlim 和 SpinWait 構造

引言

在多線程環境中,正確同步線程以便它們能夠按照正確的順序對共享對象進行操作非常重要。這一章節將介紹在 .NET 7 中實現線程同步的方法。

執行基本原子操作

.NET 7 程式碼示例

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var counter = new Counter();
        var tasks = new Task[3];

        for (int i = 0; i < 3; i++)
        {
            tasks[i] = Task.Run(() => TestCounter(counter));
        }

        await Task.WhenAll(tasks);

        Console.WriteLine($"Total count: {counter.Count}");
    }

    static void TestCounter(CounterBase c)
    {
        for (int i = 0; i < 100000; i++)
        {
            c.Increment();
            c.Decrement();
        }
    }
}

abstract class CounterBase
{
    public abstract void Increment();
    public abstract void Decrement();
}

class Counter : CounterBase
{
    private int _count;
    public int Count => _count;

    public override void Increment()
    {
        Interlocked.Increment(ref _count);
    }

    public override void Decrement()
    {
        Interlocked.Decrement(ref _count);
    }
}

使用 Mutex 構造

.NET 7 程式碼示例

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        const string MutexName = "CSharpThreadingCookbook";
        using (var m = new Mutex(false, MutexName))
        {
            if (!await Task.Run(() => m.WaitOne(TimeSpan.FromSeconds(5), false)))
            {
                Console.WriteLine("Second instance is running!");
            }
            else
            {
                Console.WriteLine("Running!");
                Console.ReadLine();
                m.ReleaseMutex();
            }
        }
    }
}

當然可以。繼續對文章進行翻譯和 .NET 7 改寫:

使用 SemaphoreSlim 構造

.NET 7 程式碼示例

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var semaphore = new SemaphoreSlim(4);
        var tasks = new Task[6];

        for (int i = 1; i <= 6; i++)
        {
            string threadName = $"Thread {i}";
            int secondsToWait = 2 + 2 * i;
            tasks[i - 1] = Task.Run(() => AccessDatabase(threadName, secondsToWait, semaphore));
        }

        await Task.WhenAll(tasks);
    }

    static async Task AccessDatabase(string name, int seconds, SemaphoreSlim semaphore)
    {
        Console.WriteLine($"{name} waits to access a database");
        await semaphore.WaitAsync();
        Console.WriteLine($"{name} was granted an access to a database");
        await Task.Delay(TimeSpan.FromSeconds(seconds));
        Console.WriteLine($"{name} is completed");
        semaphore.Release();
    }
}

使用 AutoResetEvent 和 ManualResetEventSlim 構造

.NET 7 程式碼示例

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    private static AutoResetEvent _workerEvent = new AutoResetEvent(false);
    private static ManualResetEventSlim _mainEvent = new ManualResetEventSlim(false);

    static async Task Main(string[] args)
    {
        var t = Task.Run(() => Process(10));
        Console.WriteLine("Waiting for another thread to complete work");
        _workerEvent.WaitOne();
        Console.WriteLine("First operation is completed!");
        Console.WriteLine("Performing an operation on a main thread");
        await Task.Delay(TimeSpan.FromSeconds(5));
        _mainEvent.Set();
        Console.WriteLine("Now running the second operation on a second thread");
        _workerEvent.WaitOne();
        Console.WriteLine("Second operation is completed!");
    }

    static void Process(int seconds)
    {
        Console.WriteLine("Starting a long running work...");
        Thread.Sleep(TimeSpan.FromSeconds(seconds));
        Console.WriteLine("Work is done!");
        _workerEvent.Set();
        Console.WriteLine("Waiting for a main thread to complete its work");
        _mainEvent.Wait();
        Console.WriteLine("Starting second operation...");
        Thread.Sleep(TimeSpan.FromSeconds(seconds));
        Console.WriteLine("Work is done!");
        _workerEvent.Set();
    }
}

使用 CountDownEvent 和 Barrier 構造

.NET 7 程式碼示例

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        var countdown = new CountdownEvent(2);
        var tasks = new Task[2];

        tasks[0] = Task.Run(() => PerformOperation("Operation 1 is completed", 4, countdown));
        tasks[1] = Task.Run(() => PerformOperation("Operation 2 is completed", 8, countdown));

        await Task.WhenAll(tasks);
        Console.WriteLine("Both operations have been completed.");
    }

    static void PerformOperation(string message, int seconds, CountdownEvent countdown)
    {
        Thread.Sleep(TimeSpan.FromSeconds(seconds));
        Console.WriteLine(message);
        countdown.Signal();
    }
}

使用 ReaderWriterLockSlim 構造

.NET 7 程式碼示例

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

class Program
{
    static ReaderWriterLockSlim _rw = new ReaderWriterLockSlim();
    static Dictionary<int, int> _items = new Dictionary<int, int>();

    static async Task Main(string[] args)
    {
        var readTask = Task.Run(Read);
        var writeTask1 = Task.Run(() => Write("Thread 1"));
        var writeTask2 = Task.Run(() => Write("Thread 2"));

        await Task.WhenAll(readTask, writeTask1, writeTask2);
    }

    static void Read()
    {
        Console.WriteLine("Reading contents of a dictionary");
        while (true)
        {
            try
            {
                _rw.EnterReadLock();
                foreach (var key in _items.Keys)
                {
                    Thread.Sleep(TimeSpan.FromSeconds(0.1));
                }
            }
            finally
            {
                _rw.ExitReadLock();
            }
        }
    }

    static void Write(string threadName)
    {
        while (true)
        {
            try
            {
                int newKey = new Random().Next(250);
                _rw.EnterUpgradeableReadLock();
                if (!_items.ContainsKey(newKey))
                {
                    try
                    {
                        _rw.EnterWriteLock();

當然,繼續對文章進行翻譯和 .NET 7 改寫:

### 使用 SpinWait 構造

#### .NET 7 程式碼示例
```CSharp
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static volatile bool _isCompleted = false;

    static async Task Main(string[] args)
    {
        var userModeWaitTask = Task.Run(UserModeWait);
        await Task.Delay(20);
        _isCompleted = true;

        await Task.Delay(TimeSpan.FromSeconds(1));
        _isCompleted = false;

        var hybridSpinWaitTask = Task.Run(HybridSpinWait);
        await Task.Delay(5);
        _isCompleted = true;
    }

    static void UserModeWait()
    {
        while (!_isCompleted)
        {
            Console.Write(".");
        }
        Console.WriteLine();
        Console.WriteLine("Waiting is complete");
    }

    static void HybridSpinWait()
    {
        var w = new SpinWait();
        while (!_isCompleted)
        {
            w.SpinOnce();
            Console.WriteLine(w.NextSpinWillYield);
        }
        Console.WriteLine("Waiting is complete");
    }
}

解釋

當主程序啟動時,它定義了一個將執行無窮迴圈的執行緒,直到主執行緒將 _isCompleted 變量設置為 true。然後我們使用 SpinWait 版本,它在每次迭代中都會打印出一個特殊標誌,告訴我們執行緒是否將切換到阻塞狀態。我們可以觀察到在開始時 SpinWait 嘗試在用戶模式下等待,經過約九次迭代後,它開始將執行緒切換到阻塞狀態。

以上就是針對原文中範例的 .NET 7 改寫和繁體中文翻譯。這些範例展示了如何在 .NET 7 環境中使用不同的同步機制,包括 SemaphoreSlimAutoResetEventManualResetEventSlimCountdownEventBarrierReaderWriterLockSlimSpinWait。這些機制可以幫助你在多線程程式中有效地管理資源訪問和線程同步。