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

关于C#中ReadWriterLockSlim解锁及给定异步代码的技术问询

嘿,我来帮你梳理这两个问题——先说说ReadWriterLockSlim的正确解锁姿势,再分析你给出的Refresh代码片段。

一、C#中ReadWriterLockSlim的正确解锁操作

ReadWriterLockSlim是解决读写并发问题的利器,但解锁操作如果出错,很容易导致死锁或者资源泄漏,核心原则是确保每一次锁的获取都对应一次释放,具体可以这么做:

  • 用try/finally包裹锁操作(必做)
    不管你的代码在锁内会不会抛出异常,finally块都会执行,这是最可靠的确保锁被释放的方式。举个读锁的例子:

    var rwLock = new ReadWriterLockSlim();
    rwLock.EnterReadLock();
    try
    {
        // 在这里执行你的读操作逻辑
    }
    finally
    {
        rwLock.ExitReadLock(); // 必须和Enter的锁类型对应
    }
    

    写锁和可升级读锁同理,对应EnterWriteLock()/ExitWriteLock()、EnterUpgradeableReadLock()/ExitUpgradeableReadLock(),绝对不能混用解锁方法,不然会抛出SynchronizationLockException。

  • 可以用自定义扩展实现using自动释放
    如果你觉得try/finally写起来繁琐,可以自己写个扩展方法,把锁包装成可释放的对象,这样就能用using语法自动解锁,代码更简洁:

    // 先定义一个简单的Disposable辅助类
    public class DisposableAction : IDisposable
    {
        private readonly Action _action;
        public DisposableAction(Action action) => _action = action;
        public void Dispose() => _action?.Invoke();
    }
    
    // 给ReadWriterLockSlim写扩展方法
    public static IDisposable UseReadLock(this ReadWriterLockSlim rwLock)
    {
        rwLock.EnterReadLock();
        return new DisposableAction(() => rwLock.ExitReadLock());
    }
    
    // 使用方式
    using (rwLock.UseReadLock())
    {
        // 读操作逻辑
    }
    

    写锁和可升级读锁也可以照葫芦画瓢做对应的扩展。

  • 嵌套锁要严格对应顺序
    如果必须嵌套使用锁(比如先读再升级为写),一定要保证每一次Enter都有对应的Exit,顺序不能乱。比如:

    rwLock.EnterUpgradeableReadLock();
    try
    {
        // 读操作
        rwLock.EnterWriteLock();
        try
        {
            // 写操作
        }
        finally
        {
            rwLock.ExitWriteLock();
        }
    }
    finally
    {
        rwLock.ExitUpgradeableReadLock();
    }
    
二、分析你的Refresh方法代码

先把你给出的代码片段整理一下:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
private async Task<bool> Refresh()
{
    log.Info("Refreshing Token.");
    Debug.WriteLine("Refreshing Token.");
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TOKEN_URL);
    request.Method = "POST";
    request.ContentType = "application/json; charset=UTF-8";
    request.Headers.Add("Authorization", "basic " + authCode);
    request.Accept = "application/json, text/javascript, */*; q=0.01";
    // 代码片段不完整,以下基于现有内容分析
}

我挑几个关键问题说说:

  1. CS4014警告的处理方式不推荐
    你用SuppressMessage压掉了CS4014警告,这个警告的原因是你在异步方法里调用了某个返回Task的方法但没有用await,直接把任务丢了。这种做法风险很高:

    • 如果那个未等待的任务抛出异常,在旧版.NET里会直接导致进程崩溃,新版虽然会吞掉异常,但你完全看不到错误日志,排查问题会非常困难。
    • 正确的做法:如果不需要等待任务完成,至少要处理它的异常,比如:
      _ = SomeAsyncMethod().ContinueWith(t => 
      {
          if (t.Exception != null)
          {
              log.Error("异步方法执行失败", t.Exception);
          }
      }, TaskContinuationOptions.OnlyOnFaulted);
      
      如果可以等待,那就直接加await,不要随便压制警告。
  2. 使用HttpWebRequest不如用HttpClient
    HttpWebRequest是比较老旧的API,在异步场景下,HttpClient的设计更符合现代异步编程习惯,代码更简洁,也更高效。比如你这段代码用HttpClient改写会是这样:

    private async Task<bool> Refresh()
    {
        log.Info("Refreshing Token.");
        Debug.WriteLine("Refreshing Token.");
        
        using var httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("basic", authCode);
        
        // 假设你需要发送JSON请求体
        var requestBody = new { /* 你的请求参数 */ };
        var content = new StringContent(JsonSerializer.Serialize(requestBody), Encoding.UTF8, "application/json");
        
        var response = await httpClient.PostAsync(TOKEN_URL, content);
        // 后续处理响应、判断是否刷新成功...
        return response.IsSuccessStatusCode;
    }
    

    而且HttpWebRequest的异步操作需要调用GetResponseAsync,如果你的代码后续没有正确用异步方式处理,会导致线程阻塞,浪费资源。

  3. 代码功能不完整
    目前的代码只创建了请求对象,没有写入POST请求需要的请求体,也没有获取响应内容、判断Token是否刷新成功的逻辑,这部分得补充完整,不然这个Refresh方法根本完成不了它的功能。

  4. 日志和Debug输出的搭配是合理的
    同时用log.Info(框架日志,生产环境也会输出)和Debug.WriteLine(仅Debug模式输出)是没问题的,既保证了生产环境的日志可追溯,也方便开发时调试。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:40:16