管理互斥我已經移植我的舊的HttpHandler(ashx的)通過twitterfeed代碼到的WebAPI應用。代碼的核心使用了優秀的Linq2Twitter包(https://linqtotwitter.codeplex.com/)。部分端口涉及將該組件從版本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;
}
這工作正常,但以前,我緩存了結果,以避免'過度調用'的Twitter API。在這裏,我遇到了一個問題(更多的是因爲我對異步協議的理解不夠,而不是我懷疑的其他任何東西)。
總的來說,我想要做的是首先檢查緩存,如果數據不存在,那麼補充水分緩存和數據返回給調用者(網頁)。這裏是我的代碼
public class TwitterController : ApiController {
private const string CacheKey = "TwitterFeed";
public async Task<IEnumerable<Status>>
GetTweets(int count, bool includeRetweets, bool excludeReplies)
{
var context = System.Web.HttpContext.Current;
var tweets = await GetTweetData(context, count, includeRetweets, excludeReplies);
return tweets;
}
private async Task<IEnumerable<Status>>
GetTweetData(HttpContext context, int count, bool includeRetweets, bool excludeReplies)
{
var cache = context.Cache;
Mutex mutex = null;
bool iOwnMutex = false;
IEnumerable<Status> data = (IEnumerable<Status>)cache[CacheKey];
// Start check to see if available on cache
if (data == null)
{
try
{
// Lock base on resource key
mutex = new Mutex(true, CacheKey);
// Wait until it is safe to enter (someone else might already be
// doing this), but also add 30 seconds max.
iOwnMutex = mutex.WaitOne(30000);
// Now let's see if some one else has added it...
data = (IEnumerable<Status>)cache[CacheKey];
// They did, so send it...
if (data != null)
{
return data;
}
if (iOwnMutex)
{
// Still not there, so now is the time to look for it!
data = await CallTwitterApi(count, includeRetweets, excludeReplies);
cache.Remove(CacheKey);
cache.Add(CacheKey, data, null, GetTwitterExpiryDate(),
TimeSpan.Zero, CacheItemPriority.Normal, null);
}
}
finally
{
// Release the Mutex.
if ((mutex != null) && (iOwnMutex))
{
// The following line throws the error:
// Object synchronization method was called from an
// unsynchronized block of code.
mutex.ReleaseMutex();
}
}
}
return data;
}
private DateTime GetTwitterExpiryDate()
{
string szExpiry = ConfigurationManager.AppSettings["twitterCacheExpiry"];
int expiry = Int32.Parse(szExpiry);
return DateTime.Now.AddMinutes(expiry);
}
private async Task<IEnumerable<Status>>
CallTwitterApi(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)
&& (tweet.RetweetCount < 1)
)
select tweet)
.ToListAsync();
return tweets;
}
}
在最後代碼塊中的互斥被釋放時出現問題(雖然我對GetTweetData()方法的整體格局和方式問題)嘗試:
if ((mutex != null) && (iOwnMutex))
{
// The following line throws the error:
// Object synchronization method was called from an
// unsynchronized block of code.
mutex.ReleaseMutex();
}
如果我註釋掉,代碼工作正常,但(我認爲)我應該釋放互斥鎖已經創造了它。從我發現的情況來看,這個問題與創建和釋放互斥鎖之間的線程改變有關。
因爲我缺乏異步編碼一般知識的,我不知道一),如果我使用的模式是可行的和b)如果是這樣,我怎麼解決這個問題。
任何意見將不勝感激。
謝謝斯蒂芬,非常有幫助。我試圖實現互斥鎖的原因是爲了避免來自兩個會話的兩個Twitter請求。Twitter資訊提供顯示在網站的每個頁面上,所以我推斷如果Feed需要5秒鐘的時間才能返回,那麼兩次同時請求的機率相當高,但我明白你的觀點。再次感謝您的詳細解釋和示例。 – Neilski
如果你緩存任務,那麼Twitter提要將被請求兩次的概率非常低。這是因爲任務在下載開始時立即添加到緩存中,而不是在完成時添加到緩存中。 –
這實際上非常巧妙。我假設,因爲它正在緩存一個任務,即使另一個進程在緩存下載之前向緩存發出請求,那麼它只是等待任務作出響應?我發現很難理解這甚至是可能的! – Neilski