C# 中实现 跨线程写入

方案核心思路

  1. 写入请求队列 :使用 ConcurrentQueue 接收来自任意线程的写入请求。

  2. 专用写入线程:由独立线程处理队列中的写入操作,确保顺序执行。

  3. 双信号机制 :通过 ManualResetEventSlim 控制读取线程的暂停与恢复。

  4. 线程安全确认:确保多个线程同时触发写入时,不会导致竞态条件。

完整代码实现

cs 复制代码
using System;
using System.Collections.Concurrent;
using System.Threading;

public class CrossThreadReadWriteController
{
    // 控制读取线程暂停和恢复的信号
    private readonly ManualResetEventSlim _pauseRequest = new ManualResetEventSlim(false);
    // 读取线程确认已暂停的信号
    private readonly ManualResetEventSlim _pausedConfirmed = new ManualResetEventSlim(false);
    // 写入请求队列(线程安全)
    private readonly ConcurrentQueue<Action> _writeQueue = new ConcurrentQueue<Action>();
    // 停止标志
    private volatile bool _stopRequested = false;
    // 写入专用线程
    private Thread _writeThread;

    public CrossThreadReadWriteController()
    {
        // 启动写入专用线程
        _writeThread = new Thread(ProcessWriteQueue);
        _writeThread.Start();
    }

    // 读取线程的循环任务
    public void ReadLoop()
    {
        while (!_stopRequested)
        {
            // 检查是否需要暂停
            if (_pauseRequest.IsSet)
            {
                // 确认已暂停,并等待恢复信号
                _pausedConfirmed.Set();
                _pauseRequest.Wait();
                _pausedConfirmed.Reset();
            }

            // 模拟读取操作
            Console.WriteLine($"[Read] {DateTime.Now:HH:mm:ss.fff} - Reading data...");
            Thread.Sleep(1000); // 模拟耗时操作
        }
        Console.WriteLine("[Read] Thread stopped.");
    }

    // 处理写入队列的专用线程
    private void ProcessWriteQueue()
    {
        while (!_stopRequested || !_writeQueue.IsEmpty)
        {
            if (_writeQueue.TryDequeue(out var writeAction))
            {
                // 触发暂停读取线程
                RequestPause();

                // 执行写入操作
                writeAction.Invoke();

                // 恢复读取线程
                ResumeRead();
            }
            else
            {
                Thread.Sleep(50); // 队列为空时短暂休眠
            }
        }
        Console.WriteLine("[Write] Thread stopped.");
    }

    // 跨线程提交写入请求
    public void SubmitWriteCommand(Action writeAction)
    {
        _writeQueue.Enqueue(writeAction);
    }

    // 请求暂停读取线程(线程安全)
    private void RequestPause()
    {
        _pauseRequest.Set();
        _pausedConfirmed.Wait(); // 等待读取线程确认暂停
    }

    // 恢复读取线程(线程安全)
    private void ResumeRead()
    {
        _pauseRequest.Reset();
    }

    // 停止所有线程
    public void Stop()
    {
        _stopRequested = true;
        _pauseRequest.Set(); // 确保读取线程退出等待
        _writeThread.Join(); // 等待写入线程结束
    }
}

// 使用示例
public class Program
{
    public static void Main()
    {
        var controller = new CrossThreadReadWriteController();

        // 启动读取线程
        var readThread = new Thread(controller.ReadLoop);
        readThread.Start();

        // 模拟多个线程触发写入操作
        for (int i = 0; i < 3; i++)
        {
            var threadId = i;
            new Thread(() =>
            {
                controller.SubmitWriteCommand(() =>
                {
                    Console.WriteLine($"[Write-{threadId}] {DateTime.Now:HH:mm:ss.fff} - Writing data...");
                    Thread.Sleep(500); // 模拟耗时操作
                });
            }).Start();
        }

        Thread.Sleep(5000); // 等待所有写入完成
        controller.Stop();
        readThread.Join();
        Console.WriteLine("Main thread exited.");
    }
}

关键改进解析

1. 跨线程写入请求的提交

通过 SubmitWriteCommand 方法,任何线程均可提交写入操作:

cs 复制代码
public void SubmitWriteCommand(Action writeAction)
{
    _writeQueue.Enqueue(writeAction); // 线程安全入队
}
2. 专用写入线程处理队列

写入操作由独立线程顺序处理,避免多线程并发写入冲突:

cs 复制代码
private void ProcessWriteQueue()
{
    while (!_stopRequested || !_writeQueue.IsEmpty)
    {
        if (_writeQueue.TryDequeue(out var writeAction))
        {
            RequestPause();   // 暂停读取线程
            writeAction();    // 执行写入
            ResumeRead();     // 恢复读取线程
        }
    }
}
3. 双重信号确保原子性

通过 RequestPauseResumeRead 方法封装暂停与恢复逻辑:

cs 复制代码
private void RequestPause()
{
    _pauseRequest.Set();      // 发送暂停信号
    _pausedConfirmed.Wait();  // 阻塞等待读取线程确认暂停
}
4. 线程安全停止机制

通过 _stopRequested 标志和队列检查确保安全退出:

cs 复制代码
public void Stop()
{
    _stopRequested = true;
    _writeThread.Join(); // 等待写入线程处理完队列
}

运行效果

复制代码
[Read] 14:30:01.123 - Reading data...
[Read] 14:30:02.124 - Reading data...
[Write-0] 14:30:03.125 - Writing data...
[Read] 14:30:03.626 - Reading data...
[Write-1] 14:30:04.127 - Writing data...
[Read] 14:30:04.628 - Reading data...
[Write-2] 14:30:05.129 - Writing data...
Main thread exited.

方案优势

特性 说明
多线程安全 通过 ConcurrentQueue 和信号量,支持任意线程触发写入操作。
顺序执行 写入操作由专用线程顺序处理,避免并发冲突。
无锁读取 读取线程在非写入状态下完全无锁,最大化性能。
精准控制 通过双信号机制确保写入操作执行期间读取线程完全暂停。

适用场景

  • 分布式任务调度:多个工作线程提交写入请求,由中心线程处理。

  • 实时数据采集:采集线程持续读取数据,外部线程动态更新配置。

  • 高并发服务:如网络服务器,处理来自不同客户端的并发更新操作。

注意事项

  1. 队列积压风险:若写入操作频率过高,需监控队列长度或添加背压机制。

  2. 异常处理:在写入操作中需捕获异常,避免导致写入线程崩溃。

  3. 性能调优 :可根据场景调整 Thread.Sleep 时间或使用无等待策略。

相关推荐
SelectDB2 小时前
为什么 JSON 正在成为分析数据库新的竞争点?
数据库·json·agent
ClouGence3 小时前
开源数据库管理工具 CloudDM 4.2.0 发布,新增 GoldenDB、KingbaseES 等数据源
数据库·dba·devops
发霉的馒头3 小时前
ORA-00845: MEMORY_TARGET not supported on this system的解决方法
数据库
草莓熊Lotso5 小时前
【Redis 初阶】Set 类型深度解析:去重集合的运算能力与实战场景
linux·网络·数据库·windows·redis·tcp/ip·缓存
煎饼皮皮侠7 小时前
【设计】设计一个web版的数据库管理平台后端(之五) --借鉴mybatis
数据库·mybatis
东风破_13 小时前
danci 2:创建的单词书到底存在哪里?从 Supabase 一路理解 ORM、Drizzle 和 RLS
数据库·后端·node.js
CoderIsArt13 小时前
C#中UI 线程与 Dispatcher
开发语言·ui·c#
橙子家14 小时前
OSS 文件上传的几个风险点和解决方案
数据库
2601_9620664916 小时前
【Sql Server】Update中的From语句,以及常见更新操作方式
android·java·数据库
愤怒的苹果ext17 小时前
MySQL Shell备份恢复数据库
数据库·mysql·备份恢复·mysqlsh