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

如何在单线程上运行C#并发代码?

单线程下C#协作式多任务实现方案

核心结论

  • 若依赖的是纯同步阻塞式操作(如直接调用Thread.Sleep、同步数据库查询),单线程下无法实现多任务“并发执行”——线程会被单个操作完全占用,期间无法切换到其他任务,只能串行执行。
  • 若能将阻塞逻辑改造为协作式可中断模式,或利用底层异步IO支持(即使上层封装为同步API),则可以在单线程内模拟类似操作系统轮询调度的多任务切换,实现非CPU密集型任务的“并发”效果。

可行解决方案

方案1:手动实现单线程协作式调度器

通过将任务拆分为多个可中断步骤,用状态机跟踪进度,在单线程内轮询所有任务,遇到等待逻辑时暂停当前任务、切换到下一个任务执行。

实现代码

首先定义任务接口,用于跟踪执行状态:

public interface ICooperativeTask
{
    // 返回true表示任务完成,false表示需继续执行后续步骤
    bool ExecuteStep();
}

模拟数据库调用的任务实现(用时间判断替代阻塞式Thread.Sleep,避免线程挂起):

public class DatabaseTask : ICooperativeTask
{
    private readonly string _taskId;
    private int _executionState = 0;
    private DateTime _waitEndTime;

    public DatabaseTask(string taskId)
    {
        _taskId = taskId;
    }

    public bool ExecuteStep()
    {
        switch (_executionState)
        {
            case 0:
                Console.WriteLine($"任务{_taskId} 发起数据库调用,线程ID: {Thread.CurrentThread.ManagedThreadId}");
                // 模拟数据库调用的1秒等待时长
                _waitEndTime = DateTime.Now.AddSeconds(1);
                _executionState = 1;
                return false;
            case 1:
                if (DateTime.Now >= _waitEndTime)
                {
                    Console.WriteLine($"任务{_taskId} 数据库调用完成,线程ID: {Thread.CurrentThread.ManagedThreadId}");
                    _executionState = 2;
                    return true;
                }
                return false; // 未到等待结束时间,继续轮询
            default:
                return true;
        }
    }
}

单线程调度器实现:

public static class SingleThreadTaskScheduler
{
    public static void RunAllTasks(List<ICooperativeTask> tasks)
    {
        // 轮询所有任务,直到全部完成
        while (tasks.Any(task => !task.ExecuteStep()))
        {
            // 让出CPU时间,避免无意义的忙等
            Thread.Yield();
        }
    }
}

主函数调用示例:

public static void Main()
{
    var tasks = new List<ICooperativeTask>
    {
        new DatabaseTask("1"),
        new DatabaseTask("2"),
        new DatabaseTask("3")
    };

    Console.WriteLine($"调度器运行线程ID: {Thread.CurrentThread.ManagedThreadId}");
    SingleThreadTaskScheduler.RunAllTasks(tasks);
    Console.WriteLine("所有任务执行完成");
}

方案2:异步API+自定义单线程同步上下文

若数据库驱动支持异步API(如SqlCommand.ExecuteReaderAsync),可结合async/await与自定义单线程同步上下文,让所有异步回调在同一线程执行——既利用异步IO的非阻塞特性发起并发请求,又保证全程单线程运行,不会生成额外工作线程。

实现代码

自定义单线程同步上下文:

public class SingleThreadSyncContext : SynchronizationContext, IDisposable
{
    private readonly BlockingCollection<(SendOrPostCallback Callback, object State)> _taskQueue = new();
    private readonly Thread _workerThread;

    public SingleThreadSyncContext()
    {
        _workerThread = new Thread(RunLoop) { IsBackground = true };
        _workerThread.Start();
    }

    private void RunLoop()
    {
        SetSynchronizationContext(this);
        foreach (var task in _taskQueue.GetConsumingEnumerable())
        {
            task.Callback(task.State);
        }
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        _taskQueue.Add((d, state));
    }

    public override void Send(SendOrPostCallback d, object state)
    {
        using var resetEvent = new ManualResetEvent(false);
        Post(_ =>
        {
            try { d(state); }
            finally { resetEvent.Set(); }
        }, null);
        resetEvent.WaitOne();
    }

    public void Dispose()
    {
        _taskQueue.CompleteAdding();
        _workerThread.Join();
        _taskQueue.Dispose();
    }
}

异步数据库调用与调度:

public static async Task ExecuteDatabaseCallAsync(string taskId)
{
    Console.WriteLine($"任务{taskId} 发起异步数据库请求,线程ID: {Thread.CurrentThread.ManagedThreadId}");
    // 替换为实际异步数据库操作,如await dbCommand.ExecuteReaderAsync()
    await Task.Delay(1000);
    Console.WriteLine($"任务{taskId} 异步请求完成,线程ID: {Thread.CurrentThread.ManagedThreadId}");
}

public static void Main()
{
    using var syncContext = new SingleThreadSyncContext();
    Console.WriteLine($"主启动线程ID: {Thread.CurrentThread.ManagedThreadId}");

    var task1 = ExecuteDatabaseCallAsync("1");
    var task2 = ExecuteDatabaseCallAsync("2");
    var task3 = ExecuteDatabaseCallAsync("3");

    // 等待所有任务完成(回调均在单线程内执行)
    Task.WhenAll(task1, task2, task3).Wait();
    Console.WriteLine("所有任务执行完成");
}

关键限制说明

如果必须使用纯同步阻塞的数据库调用(无异步API可用),则单线程下只能串行执行任务——任何阻塞操作都会占用线程直至完成,无法切换到其他任务,此时你的示例中method 1是唯一可行方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 03:31:03