You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#多线程任务同步问题求助:早餐任务模拟场景

多线程早餐任务模拟问题解决

原代码的问题分析

你的while循环条件写错了:while (!done1 && !done2) 意味着只有当两个任务组都未处理时才循环,只要其中一个任务组完成(比如烤面包先结束,done2变为true),循环就会立即退出,导致另一个任务组的后续任务(比如制作加奶咖啡)永远无法启动。

另外,用无限轮询线程状态的方式不仅效率低,还会占用CPU资源,应该使用.NET提供的同步机制来等待线程完成,而不是手动轮询。

方案1:修复原Thread代码,使用同步事件

可以用ManualResetEvent来等待线程完成,避免轮询:

using System.Threading;

class Parameters
{
    public string? task { get; set; }
    public int time { get; set; }

    public Parameters(string? task, int time)
    {
        this.time = time;
        this.task = task;
    }
}

class Program
{
    static Parameters[] p = new Parameters[6];

    static void Main(string[] args)
    {
        p[0] = new Parameters("Prepare the coffee", 10000);
        p[1] = new Parameters("Heat the milk", 12000);
        p[2] = new Parameters("Prepare the toast", 6000);
        p[3] = new Parameters("Prepare the coffee with milk", 12000);
        p[4] = new Parameters("Spread the butter on the toast", 15000);
        p[5] = new Parameters("Have breakfast", 20000);

        // 创建同步事件,用于标记线程完成
        var coffeeDone = new ManualResetEvent(false);
        var milkDone = new ManualResetEvent(false);
        var toastDone = new ManualResetEvent(false);

        Thread t1 = new Thread(() => {
            task(p[0]);
            coffeeDone.Set(); // 标记煮咖啡完成
        });
        Thread t2 = new Thread(() => {
            task(p[1]);
            milkDone.Set(); // 标记热牛奶完成
        });
        Thread t3 = new Thread(() => {
            task(p[2]);
            toastDone.Set(); // 标记烤面包完成
        });
        Thread t4 = new Thread(task);
        Thread t5 = new Thread(task);
        Thread t6 = new Thread(() => task(p[5]));

        // 启动初始三个任务
        t1.Start();
        t2.Start();
        t3.Start();

        // 等待煮咖啡和热牛奶都完成,然后启动制作加奶咖啡
        WaitHandle.WaitAll(new[] { coffeeDone, milkDone });
        t4.Start(p[3]);

        // 等待烤面包完成,启动涂黄油
        toastDone.WaitOne();
        t5.Start(p[4]);

        // 等待所有准备任务完成,启动吃早餐
        t4.Join();
        t5.Join();
        t6.Start();
    }

    static void task(Parameters p)
    {
        Console.WriteLine("Starting task: " + p.task);
        Thread.Sleep(p.time);
        Console.WriteLine("Ending task: " + p.task);
    }
}

方案2:使用更现代的Task异步编程(推荐)

.NET的Task API比直接使用Thread更简洁易用,支持异步等待,无需手动管理同步事件:

using System;
using System.Threading.Tasks;

class Parameters
{
    public string? task { get; set; }
    public int time { get; set; }

    public Parameters(string? task, int time)
    {
        this.time = time;
        this.task = task;
    }
}

class Program
{
    static async Task Main(string[] args)
    {
        var tasks = new Parameters[]
        {
            new Parameters("Prepare the coffee", 10000),
            new Parameters("Heat the milk", 12000),
            new Parameters("Prepare the toast", 6000),
            new Parameters("Prepare the coffee with milk", 12000),
            new Parameters("Spread the butter on the toast", 15000),
            new Parameters("Have breakfast", 20000)
        };

        // 启动初始三个任务
        var coffeeTask = Task.Run(() => ExecuteTask(tasks[0]));
        var milkTask = Task.Run(() => ExecuteTask(tasks[1]));
        var toastTask = Task.Run(() => ExecuteTask(tasks[2]));

        // 等待咖啡和牛奶都完成后,启动制作加奶咖啡
        await Task.WhenAll(coffeeTask, milkTask);
        var coffeeWithMilkTask = Task.Run(() => ExecuteTask(tasks[3]));

        // 等待烤面包完成后,启动涂黄油
        await toastTask;
        var butterToastTask = Task.Run(() => ExecuteTask(tasks[4]));

        // 等待所有准备任务完成,启动吃早餐
        await Task.WhenAll(coffeeWithMilkTask, butterToastTask);
        await Task.Run(() => ExecuteTask(tasks[5]));
    }

    static void ExecuteTask(Parameters p)
    {
        Console.WriteLine("Starting task: " + p.task);
        Task.Delay(p.time).Wait(); // 用Task.Delay模拟耗时,比Thread.Sleep更友好
        Console.WriteLine("Ending task: " + p.task);
    }
}

关键说明

  1. 原代码的循环条件错误:即使改为while (!done1 || !done2)修复轮询逻辑,手动轮询依然是低效方案,优先使用同步事件或Task异步等待。
  2. ManualResetEvent可以安全地等待线程完成,避免CPU空转。
  3. Task API是.NET推荐的异步编程方式,代码更简洁,支持await、Task.WhenAll等便捷方法,无需手动管理线程和同步。

内容的提问来源于stack exchange,提问作者Manel Ros

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 02:46:07