如何实现多线程变量访问?现有双线程交互方案是否有优化空间?
更优实现方案分析
你当前的实现存在两个明显的不足:
- TaskB通过循环sleep轮询的方式等待响应,属于忙等待,会无意义消耗CPU资源
- 仅依赖
volatile关键字保证内存可见性,在复杂场景(比如多请求叠加、高并发)下可能出现竞态问题,缺乏可靠的同步机制
下面提供几种更高效、健壮的替代实现:
方案1:使用ManualResetEventSlim(同步阻塞式)
利用轻量级的事件通知机制替代轮询,彻底消除忙等待的CPU浪费:
class RequestResponse { public string Data { get; set; } public bool IsRequested { get; set; } private readonly ManualResetEventSlim _responseReadyEvent = new ManualResetEventSlim(false); // TaskA处理完成后触发通知 public void NotifyResponseReady() { _responseReadyEvent.Set(); } // TaskB等待响应,阻塞期间不占用CPU public void WaitForResponse() { _responseReadyEvent.Wait(); _responseReadyEvent.Reset(); // 重置事件,准备下一次请求 } } class TaskA { public void Run(RequestResponse reqResp) { while (true) { if (reqResp.IsRequested) { reqResp.Data = "something"; reqResp.NotifyResponseReady(); reqResp.IsRequested = false; } this.DoSomething(); } } } class TaskB { public void Run(RequestResponse reqResp) { while (true) { reqResp.IsRequested = true; reqResp.WaitForResponse(); // 处理响应数据 Console.WriteLine(reqResp.Data); } } }
方案2:使用TaskCompletionSource(异步非阻塞式)
适合异步编程场景,避免线程阻塞,提升系统资源利用率:
class AsyncRequestHandler { private TaskCompletionSource<string> _pendingRequest; // TaskB发起异步请求,返回等待响应的Task public Task<string> SendRequest() { _pendingRequest = new TaskCompletionSource<string>(); return _pendingRequest.Task; } // TaskA处理请求并返回结果 public void ProcessPendingRequest() { if (_pendingRequest != null && !_pendingRequest.Task.IsCompleted) { _pendingRequest.SetResult("something"); } } } class TaskA { public async Task Run(AsyncRequestHandler handler) { while (true) { handler.ProcessPendingRequest(); await Task.Delay(10); // 模拟DoSomething的异步耗时操作 } } } class TaskB { public async Task Run(AsyncRequestHandler handler) { while (true) { string response = await handler.SendRequest(); // 处理响应 Console.WriteLine(response); await Task.Delay(100); // 模拟请求间隔 } } }
方案3:使用BlockingCollection(消息队列式)
将请求和响应抽象为消息,通过队列传递,天然支持多生产者/消费者,扩展性更强:
// 请求消息(可扩展携带请求参数) class RequestMessage { } // 响应消息 class ResponseMessage { public string Data { get; set; } } class TaskA { private readonly BlockingCollection<RequestMessage> _requestQueue; private readonly BlockingCollection<ResponseMessage> _responseQueue; public TaskA(BlockingCollection<RequestMessage> requestQueue, BlockingCollection<ResponseMessage> responseQueue) { _requestQueue = requestQueue; _responseQueue = responseQueue; } public void Run() { while (true) { // 尝试读取请求(非阻塞,不影响DoSomething执行) if (_requestQueue.TryTake(out var _)) { _responseQueue.Add(new ResponseMessage { Data = "something" }); } this.DoSomething(); } } } class TaskB { private readonly BlockingCollection<RequestMessage> _requestQueue; private readonly BlockingCollection<ResponseMessage> _responseQueue; public TaskB(BlockingCollection<RequestMessage> requestQueue, BlockingCollection<ResponseMessage> responseQueue) { _requestQueue = requestQueue; _responseQueue = responseQueue; } public void Run() { while (true) { _requestQueue.Add(new RequestMessage()); var response = _responseQueue.Take(); // 阻塞等待响应,无CPU浪费 // 处理响应 Console.WriteLine(response.Data); } } }
方案选型建议
- 如果是简单的同步交互场景,ManualResetEventSlim实现简单直观,CPU占用低
- 如果项目采用异步编程模型,TaskCompletionSource能避免线程阻塞,提升吞吐量
- 如果需要支持多任务、批量消息交互,BlockingCollection的扩展性和可靠性更优
内容的提问来源于stack exchange,提问作者Kiran Ramesh
相关产品推荐
相关产品推荐

