C# 如何在异步方法中管理互斥体

C# 如何在异步方法中管理互斥体,c#,asynchronous,synchronization,mutex,linq-to-twitter,C#,Asynchronous,Synchronization,Mutex,Linq To Twitter,我已经将旧的HttpHandler(.ashx)TwitterFeed代码移植到WebAPI应用程序。代码的核心使用了优秀的Linq2Twitter包()。端口的一部分涉及将该组件从版本2升级到版本3,该版本现在提供了许多异步方法调用,这对我来说是新的。以下是基本控制器: public async Task<IEnumerable<Status>> GetTweets(int count, bool includeRetweets, bool excludeReplie

我已经将旧的HttpHandler(.ashx)TwitterFeed代码移植到WebAPI应用程序。代码的核心使用了优秀的Linq2Twitter包()。端口的一部分涉及将该组件从版本2升级到版本3,该版本现在提供了许多异步方法调用,这对我来说是新的。以下是基本控制器:

public async Task<IEnumerable<Status>> 
GetTweets(int count, bool includeRetweets, bool excludeReplies)
{
   var auth = new SingleUserAuthorizer
   {
      CredentialStore = new SingleUserInMemoryCredentialStore
      {
         ConsumerKey       = ConfigurationManager.AppSettings["twitterConsumerKey"],
         ConsumerSecret    = ConfigurationManager.AppSettings["twitterConsumerKeySecret"],
         AccessToken       = ConfigurationManager.AppSettings["twitterAccessToken"],
         AccessTokenSecret = ConfigurationManager.AppSettings["twitterAccessTokenSecret"]
      }
   };

   var ctx = new TwitterContext(auth);

   var tweets =
      await
      (from tweet in ctx.Status
         where (
            (tweet.Type == StatusType.Home)
            && (tweet.ExcludeReplies == excludeReplies)
            && (tweet.IncludeMyRetweet == includeRetweets)
            && (tweet.Count == count)
         )
      select tweet)
      .ToListAsync();

   return tweets;
}
如果我注释掉这行代码,代码工作正常,但是(我假设)我应该释放创建它的互斥锁。据我所知,这个问题与创建和释放互斥锁之间的线程变化有关

由于我缺乏异步编码的一般知识,我不确定a)我使用的模式是否可行,b)如果可行,我如何解决问题


任何建议都将不胜感激。

像这样使用互斥锁是行不通的。首先,
Mutex
是线程仿射的,因此不能与
async
代码一起使用

我注意到的其他问题:

  • 缓存
    是线程安全的,因此它无论如何都不需要互斥锁(或任何其他保护)
  • 异步方法应遵循以下步骤
关于缓存有一个主要提示:当您只有内存缓存时,则缓存任务而不是结果数据。顺便说一句,我想知道
HttpContext.Cache
是否是最好的缓存,但我会保持原样,因为您的问题更多的是异步代码如何改变缓存模式

因此,我推荐如下:

private const string CacheKey = "TwitterFeed";

public Task<IEnumerable<Status>> GetTweetsAsync(int count, bool includeRetweets, bool excludeReplies)
{
  var context = System.Web.HttpContext.Current;
  return GetTweetDataAsync(context, count, includeRetweets, excludeReplies);
}

private Task<IEnumerable<Status>> GetTweetDataAsync(HttpContext context, int count, bool includeRetweets, bool excludeReplies)
{
  var cache = context.Cache;
  Task<IEnumerable<Status>> data = cache[CacheKey] as Task<IEnumerable<Status>>;
  if (data != null)
    return data;
  data = CallTwitterApiAsync(count, includeRetweets, excludeReplies);
  cache.Insert(CacheKey, data, null, GetTwitterExpiryDate(), TimeSpan.Zero);
  return data;
}

private async Task<IEnumerable<Status>> CallTwitterApiAsync(int count, bool includeRetweets, bool excludeReplies)
{
  ...
}
private const string CacheKey=“TwitterFeed”;
公共任务GetTweetsAsync(整数计数、bool includeTweets、bool excludeReplies)
{
var context=System.Web.HttpContext.Current;
返回GetTweetDataAsync(上下文、计数、IncludeTweets、excludeReplies);
}
私有任务GetTweetDataAsync(HttpContext上下文、int计数、bool includeReteets、bool excludeReplies)
{
var cache=context.cache;
任务数据=缓存[CacheKey]作为任务;
如果(数据!=null)
返回数据;
数据=CallTwitterApiaAsync(计数,包括推特,不包括回复);
Insert(CacheKey,data,null,GetTwitterExpiryDate(),TimeSpan.Zero);
返回数据;
}
专用异步任务CallTwitterApiaAsync(整数计数、bool includeTweets、bool excludeReplies)
{
...
}

如果两个不同的请求(来自两个不同的会话)在相同的时间请求相同的twitter提要,那么该提要将被请求两次的可能性很小。但我不会为此而失眠。

谢谢你,斯蒂芬,非常有帮助。我尝试实现互斥的原因是为了避免来自两个会话的两个Twitter请求。Twitter提要显示在网站的每个页面上,因此我推断,如果提要花了5秒钟返回,那么两个同时请求的可能性相当高,但我接受你的观点。再次非常感谢您的详细解释和示例。如果您缓存任务,那么Twitter提要被请求两次的可能性非常低。这是因为任务在下载开始时就被添加到缓存中,而不是在下载完成时。这实际上非常聪明。我假设,因为它正在缓存一个任务,即使另一个进程在下载之前对缓存发出请求,那么它只是等待任务响应?我发现很难理解这是可能的!对将(未完成的)任务添加到缓存后,任何其他线程都可以从缓存中读取该任务。一个任务可以由任意数量的方法同时等待,当任务完成时,它们都将继续。而且,任务完成后,等待任务不会让步;取而代之的是,wait跟随一个命令并继续同步执行。所以我喜欢将任务存储在缓存中,但这只能通过内存缓存来完成。
if ((mutex != null) && (iOwnMutex))
{
    // The following line throws the error:
    // Object synchronization method was called from an
    // unsynchronized block of code.
    mutex.ReleaseMutex();
}
private const string CacheKey = "TwitterFeed";

public Task<IEnumerable<Status>> GetTweetsAsync(int count, bool includeRetweets, bool excludeReplies)
{
  var context = System.Web.HttpContext.Current;
  return GetTweetDataAsync(context, count, includeRetweets, excludeReplies);
}

private Task<IEnumerable<Status>> GetTweetDataAsync(HttpContext context, int count, bool includeRetweets, bool excludeReplies)
{
  var cache = context.Cache;
  Task<IEnumerable<Status>> data = cache[CacheKey] as Task<IEnumerable<Status>>;
  if (data != null)
    return data;
  data = CallTwitterApiAsync(count, includeRetweets, excludeReplies);
  cache.Insert(CacheKey, data, null, GetTwitterExpiryDate(), TimeSpan.Zero);
  return data;
}

private async Task<IEnumerable<Status>> CallTwitterApiAsync(int count, bool includeRetweets, bool excludeReplies)
{
  ...
}