开发者

C#多线程同步不同实现方式小结

目录
  • AutoResetEvent
    • 代码功能概述:
    • 代码执行流程:
  • ManualResetEvent
    • 代码功能概述:
    • 代码执行流程:
  • CountdownEvent
    • Barrier
      • 代码功能概述:
      • 运行结果

    AutoResetEvent

    class MainClass
    {
    	// the array of consumer threads
    	private static List<Thread> consumers = new List<Thread> ();
    
    	// the task queue
    	private static Queue<Action> tasks = new Queue<Action>();
    
    	// the synchronisation object for locking the task queue
    	private static readonly object queueLock = new object();
    
    	// this wait handle notifies consumers of a new task
    	private static EventWaitHandle newTaskAvailable = new AutoResetEvent (false);
    
    	// the synchronisation object for locking the console color
    	private static readonly object consoleLock = new object();
    
    
    	// enqueue a new task
    	private static void EnqueueTask (Action task)
    php	{
    		lock (queueLock)
    		{
    			tasks.Enqueue (task);
    		}
    		newTaskAvailable.Set();
    	}
    
    
    	// thread work method for consumers
    	private static void DoWork(ConsoleColor color)
    	{
    		while (true)
    		{
    			// get a new task
    			Action task = null;
    			lock (queueLock) {
    				if (tasks.Count > 0)
    				{
    					task = tasks.Dequeue ();
    				}
    			}
    			if (task != null)
    			{
    				// set console to this task's color
    				lock (consoleLock)
    				{
    					Console.ForegroundColor = color;
    				}
    
    				// execute task
    				task ();
    			}
    			else
    				// queue is empty - wait for a new task
    				newTaskAvailable.WaitOne();
    		}
    	}
    
    	public static void Main (string[] args)
    	{
    		// set up 3 consumers
    		consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Red); }));
    		consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Green); }));
    		consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Blue); }));
    
    		// start all consumers
    		consumers.ForEach ( (t) => { t.Start (); });
    
    		while (true)js
    		{
    			// add a new task
    			Random r = new Random();
    			EnqueueTask ( () => {
    
    				// the task is to write a random number to the console
    				int number = r.Next (10);
    				Console.Write (number);
    
    			});
    
    			// random sleep to simulate workload
    			Thread.Sleep (r.Next (1000)); 
    		}
    	}
    }
    

    这段代码实现了一个简单的生产者-消费者模型,其中有一个生产者(在Main方法中模拟)和多个消费者(在这个例子中是三个线程,每个线程代表一个消费者)。这个模型通过共享的任务队列来同步生产者和消费者之间的工作。

    代码功能概述:

    • 任务队列:使用Queue<Action>来存储待执行的任务(在这里,任务被定义为无参数的Action委托)。

    • 生产者:在Main方法中,通过无限循环不断生成新的任务(打印一个随机数到控制台),并将这些任务添加到任务队列中。生产者每生成一个任务后,会随机等待一段时间(模拟工作负载)。

    • 消费者:有三个消费者线程,每个线程都执行相同的DoWork方法,但传入不同的控制台颜色参数。消费者线程从任务队列中取出任务并执行(即调用任务委托),同时在执行任务前将控制台颜色设置为自己的颜色。如果任务队列为空,消费者线程将等待(通过newTaskAvailable.WaitOne()),直到生产者通知它们有新的任务可用。

    • 同步机制

      • 使用queueLock对象来同步对任务队列的访问,确保在添加或移除任务时不会发生数据竞争。
      • 使用newTaskAvailableAutoResetEvent)来通知消费者有新的任务可用。当生产者将任务放入队列后,它会设置这个事件,从而唤醒一个等待的消费者线程。
      • 使用consoleLock对象来同步对控制台颜色的设置,防止多个消费者线程同时更改控制台颜色。

    代码执行流程:

    • 初始化:在Main方法中,创建并启动三个消费者线程,每个线程都执行DoWork方法,但传入不同的控制台颜色参数。

    • 生产者循环Main方法进入一个无限循环,不断生成新的任务(打印随机数到控制台),并将这些任务添加到任务队列中。每生成一个任务后,生产者会随机等待一段时间。

    • 消费者工作:每个消费者线程都进入一个无限循环,尝试从任务队列中取出任务。如果队列不为空,它们将设置控制台颜色为自己的颜色,执行任务(打印随机数),然后再次尝试从队列中取出任务。如果队列为空,它们将等待newTaskAvailable事件被设置,这通常发生在生产者将新任务添加到队列后。

    • 持续运行:这个过程将持续进行,因为生产者和消费者都运行在无限循环中。在实际应用中,您可能需要添加某种退出机制来优雅地停止程序。

    ManualResetEvent

    class MainClass
    {
    	// the array of consumer threads
    	private static List<Thread> consumers = new List<Thread> ();
    
    	// the task queue
    	private static Queue<Action> tasks = new Queue<Action>();
    
    	// the synchronisation object for locking the task queue
    	private static readonly object queueLock = new object();
    
    	// this wait handle notifies consumers of a new task
    	private static EventWaitHandle newTaskAvailable = new AutoResetEvent (false);
    
    	// this wait handle pauses consumers
    	private static EventWaitHandle pauseConsumers = new ManualResetEvent (true);
    
    	// the synchronisation object for locking the console color
    	private static readonly object consoleLock = new object();
    
    
    	// enqueue a new task
    	private static void EnqueueTask (Action task)
    	{
    		lock (queueLock)
    		{
    			tasks.Enqueue (taskphp);
    		}
    		newTaskAvailable.Set();
    	}
    
    
    	// thread work method for consumers
    	private static void DoWork(ConsoleColor color)
    	{
    		while (true)
    		{
    			// check if producer asked us to pause
    			pauseConsumers.WaitOne ();
    
    			// get a new task
    			Action task = null;
    			lock (queueLock) {
    				if (tasks.Count > 0)
    				{
    					task = tasks.Dequeue ();
    				}
    			}
    			if (task != null)
    			{
    				// set console to this task's color
    				lock (consoleLock)
    				{
    					Console.ForegroundColor = color;
    				}
    
    				// execute task
    				task ();
    			}
    			else
    				// queue is empty - wait for a new task
    				newTaskAvailable.WaitOne();
    		}
    	}
    
    	public static void Main (string[] args)
    	{
    		// set up 3 consumers
    		consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Red); }));
    		consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Green); }));
    		consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Blue); }));
    
    		// start all consumers
    		consumers.ForEach ( (t) => { t.Start (); });
    
    		bool consumersPaused = false;
    
    		while (true)
    		{
    			// add a new task
    			Random r = new Random();
    			EnqueueTask ( () => {
    
    				// the task is to write a random number to the console
    				int number = r.Next (10);
    				Console.Write (number);
    
    			});
    
    			// random sleep to simulate workload
    			Thread.Sleep (r.Next (1000)); 
    
    			// pressing any key pauses/unpauses the consumers
    			if (Console.KeyAvailable)
    			{
    				Console.Read ();
    				if (consumersPaused)
    				{
    					pauseConsumers.Set ();
    					Console.WriteLine ("Consumers resumed");
    				}
    				else
    				{
    					pauseConsumers.Reset ();
    					Console.WriteLine ("Consumers paused");
    				}
    
    				consumersPaused = !consumersPaused;
    			}
    		}
    	}
    }
    

    这段代码实现了一个带有暂停/恢复功能的生产者-消费者模型,其中包含一个生产者(在Main方法中模拟)和三个消费者线程。这个模型通过共享的任务队列来同步生产者和消费者之间的工作,并且允许用户通过按键操作来暂停或恢复消费者的执行。

    代码功能概述:

    • 任务队列:使用Queue<Action>来存储待执行的任务。

    • 同步机制

      • queueLock:用于同步对任务队列的访问,确保在添加或移除任务时不会发生数据竞争。
      • newTaskAvailableAutoResetEvent):当生产者将新任务添加到队列时,设置此事件以通知一个等待的消费者线程。
      • pauseConsumersManualResetEvent):允许生产者(或用户)暂停和恢复消费者的执行。
      • consoleLock:用于同步对控制台颜色的设置,防止多个消费者线程同时更改控制台颜色。
    • 消费者线程:三个消费者线程分别执行DoWork方法,但传入不同的控制台颜色参数。消费者线程无限循环地等待新任务,如果任务队列中有任务,则取出任务并执行,同时设置控制台颜色为自己的颜色。如果任务队列为空,则等待newTaskAvailable事件被设置。此外,消费者还会检查pauseConsumers事件是否被设置,如果被设置,则暂停执行直到被重置。

    • 生产者:在Main方法中模拟,不断生成新的任务(打印一个随机数到控制台)并添加到任务队列中。每生成一个任务后,生产者会随机等待一段时间(模拟工作负载)。此外,生产者还检查控制台是否有按键输入,如果有,则切换消费者的暂停/恢复状态。

    代码执行流程:

    • 初始化:创建并启动三个消费者线程,每个线程都执行DoWork方法,但传入不同的控制台颜色参数。

    • 生产者循环Main方法进入一个无限循环,不断生成新的任务(打印随机数到控制台),并将这些任务添加到任务队列中。每生成一个任务后,生产者会随机等待一段时间。同时,生产者检查控制台是否有按键输入,并根据按键切换消费者的暂停/恢复状态。

    • 消费者工作:每个消费者线程都进入一个无限循环,首先检查pauseConsumers事件是否被设置,如果被设置则等待。然后尝试从任务队列中取出任务,如果队列不为空,则设置控制台颜色并执行任务;如果队列为空,则等待newTaskAvailable事件被设置。

    • 暂停/恢复:用户可以通过按任意键来切换消费者的暂停/恢复状态。如果消费者当前是暂停状态,则按键将唤醒它们并继续执行;如果消费者当前是运行状态,则按键将使它们暂停执行。

    CountdownEvent

    class MainClass
    {
    	// the array of consumer threads
    	private static List<Thread> consumers = new List<Thread> ();
    
    	// the task queue
    	private static Queue<Action> tasks = new Queue<Action>();
    
    	// the synchronisation object for locking the task queue
    	private static readonly object queueLock = new object();
    
    	// this wait handle notifies consumers of a new task
    	private static EventWaitHandle newTaskAvailable = new AutoResetEvent (false);
    
    	// the wait handle to quit consumers
    	private static CountdownEvent quitConsumers = new CountdownEvent (3);
    
    	// the flag to request that consumers quit
    	private static bool quitRequested = false;
    
    	// the synchronisation object for quitting consumers
    	private static readonly object quitLock = new object ();
    
    	// the synchronisation object for locking the console color
    	private static readonly object consoleLock = new object();
    
    
    	// enqueue a new task
    	private static void EnqueueTask (Action task)
    	{
    		lock (queueLock)
    		{
    			tasks.Enqueue (task);
    		}
    		newTaskAvailable.Set();
    	}
    
    
    	// thread work method for consumers
    	private static void DoWork(ConsoleColor color)
    	{
    		while (true)
    		{
    			//javascript check if someone asked us to quit
    			lock (quitLock)
    			{
    				if (quitRequested)
    				{
    					Console.WriteLine ("Consumer {0} is quitting", color);
    					quitConsumers.Signal ();
    					break;
    				}
    			}
    
    			// get a new task
    			Action task = null;
    			lock (queueLock) {
    				if (tasks.Count > 0)
    				{
    					task = tasks.Dequeue ();
    				}
    			}
    			if (task != null)
    			{
    				// set console to this task's color
    				lock (consoleLock)
    				{
    					Console.ForegroundColor = color;
    				}
    
    				// execute task
    				task ();
    			}
    			else
    				// queue is empty - wait for a new task
    				newTaskAvailable.WaitOne(1000);
    		}
    	}
    
    	public static void Main (string[] args)
    	{
    		// set up 3 consumers
    		consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Red); }));
    		consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Green); }));
    		consumers.Add(new Thread ( () => { DoWork(ConsoleColor.Blue); }));
    
    		// start all consumers
    		cpythononsumers.ForEach ( (t) => { t.Start (); });
    
    		int iterations = 0;
    
    		while (true)
    		{
    			// add a new task
    			Random r = new Random();
    			EnqueueTask ( () => {
    
    				// the task is to write a random number to the console
    				int number = r.Next (10);
    				Console.Write (number);
    
    			});
    
    			// random sleep to simulate workload
    			Thread.Sleep (r.Next (1000)); 
    
    			// quit after 10 iterations
    			if (iterations++ >= 10)
    			{
    				// request consumer quit
    				lock (quitLock)
    				{
    					quitRequested = true;
    				}
    
    				// wait until all consumers have signalled
    				quitConsumers.Wait ();
    
    				Console.WriteLine ("All consumers have quit");
    				break;
    			}
    		}
    	}
    }
    

    这个代码实现了一个带有优雅退出机制的生产者-消费者模型。它创建了三个消费者线程,这些线程从共享的任务队列中取出并执行任务。与之前的代码相比,这个代码添加了以下主要功能和改进:

    • 优雅退出机制

      • 引入了quitRequested标志和quitLock同步对象,用于控制消费者线程的退出请求。
      • 使用CountdownEventquitConsumers)来等待所有消费者线程都完成退出前的清理工作并发出信号。
      • 当生产者决定退出时,它会将quitRequested标志设置为true,并通过quitConsumers.Wait()等待所有消费者线程都调用quitConsumers.Signal()来确认它们已经准备好退出。
    • 消费者线程的退出逻辑

      • 每个消费者线程在循环开始时都会检查quitRequested标志。如果设置为true,则消费者将打印一条退出消息,调用quitConsumers.Signal()来通知生产者它已准备好退出,并退出循环。
      • 注意,消费者在退出前会释放控制台颜色的锁(consoleLock),但在这个特定的例子中,由于退出是在没有任务可执行的空闲时间发生的,所以这一步实际上可能是多余的,因为退出时不会再次访问控制台颜色。
    • 任务队列的轮询

      • 消费者在任务队列为空时会调用newTaskAvailable.WaitOne(1000),这是一个带超时的等待调用。这意味着如果1000毫秒内没有新任务到来,消费者将停止等待并再次检查退出条件。这有助于防止消费者线程在队列为空时永久挂起。
    • 生产者的迭代次数限制

      • 生产者在一个循环中运行,但只执行有限次数的迭代(在这个例子中是10次)。每次迭代都会向任务队列中添加一个新任务,并在迭代之间随机等待一段时间以模拟工作负载。
      • 当达到迭代次数限制时,生产者会请求消费者退出,并等待所有消费者都准备好退出。
    • 其他同步机制

      • 代码仍然使用queueLock来同步对任务队列的访问,以防止数据竞争。
      • consoleLock用于同步对控制台颜色的访问,以确保当多个消费者线程尝试同时更改控制台颜色时不会发生冲突。
    • 代码执行流程

      • 程序初始化三个消费者线程,并将它们启动。
      • 生产者在一个循环中运行,每次迭代都向任务队列中添加一个新任务,并随机等待一段时间。
      • 当达到迭代次数限制时,生产者请求消费者退出,并等待它们确认。
      • 一旦所有消费者都确认退出,生产者将打印一条消息,并退出程序。

    Barrier

    class MainClass
    {
        // wait handles to rendezvous threads  
        public static Barrier barrier = new Barrier(3, b => Console.WriteLine("All threads have reached the barrier."));
    
        // thread work method  
        public static void DoWork()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.Write(Thread.CurrentThread.ManagedThreadId + ": " + i + " ");
    
                // rendezvous with other threads  
                barrier.SignalAndwait();
            }
        }
    
        public static void Main(string[] args)
        {
            // start three threads  
            new Thread(DoWork).Start();
            new Thread(DoWork).Start();
            new Thread(DoWork).Start();
    
            // Keep the main thread alive to prevent the program from exiting before the threads finish  
            Console.WriteLine("Press Enter to exit...");
            Console.ReadLine();
        }
    }
    

    代码功能概述:

    • Barrier 初始化

      • 在 MainClass 中,创建了一个 Barrier 实例 barrier,其构造函数接受两个参数:参与同步的线程数(这里是 3)和一个在每次所有线程都到达屏障时调用的委托(打印一条消息)。
    • 线程工作方法

      • DoWork 方法是三个线程将执行的方法。每个线程都会进入一个循环,循环 5 次。
      • 在每次循环迭代中,线程都会打印其当前迭代次数和线程 ID,然后调用 barrier.SignalAndWait()
      • SignalAndWait 方法导致当前线程在屏障处等待,直到所有其他参与线程也都调用了 SignalAndWait。一旦所有线程都到达屏障,它们会同时继续执行,并且如果提供了,会执行构造函数中指定的委托(打印一条消息)。
    • 主线程

      • 主线程启动了三个 DoWork 线程,并等待用户按下 Enter 键以继续执行。这是为了防止主线程在后台线程完成之前退出程序。

    运行结果

    • 当所有三个线程都到达 barrier.SignalAndWait() 时,它们会同时停止执行,并等待彼此。
    • 一旦所有线程都到达屏障,它们会同时继续执行,并且控制台会打印出 “All threads have reached the barrier.”。
    • 这个过程会在每次循环迭代时重复,直到每个线程都完成了 5 次迭代。
    • 最后,用户按下 Enter 键后,程序退出。

    到此这篇关于C#多线程同步不同实现方式小结的文章就介绍到这了,更多相关C# 多线程同步内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)! 

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜