이전 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;
}
이 잘 작동하지만 이전에, 나는 트위터 API '를 호출을 통해'방지하기 위해 결과를 캐시했다. 여기에 내가 문제가 생겼다. (내가 의심하는 것보다 비동기 프로토콜에 대한 나의 이해가 부족하다.)
개요에서 데이터를 찾지 못하면 캐시를 먼저 확인한 다음 캐시를 다시 확보하고 호출자 (웹 페이지)로 데이터를 반환합니다.
if ((mutex != null) && (iOwnMutex))
{
// The following line throws the error:
// Object synchronization method was called from an
// unsynchronized block of code.
mutex.ReleaseMutex();
}
: 여기에 코드 (필자는 GetTweetData() 메소드의 전체적인 패턴과 접근 방식에 대한 우려가 있지만) 문제는 뮤텍스가 해제되고 마지막 코드 블록에서 발생
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;
}
}
에서 내 시도이다
줄을 주석 처리하면 코드가 제대로 작동하지만 (실제로는) 내가 만든 뮤텍스를 릴리스해야합니다. 내가 발견 한 바에 따르면,이 문제는 뮤텍스를 생성하고 릴리스하는 사이에 변경되는 스레드와 관련이 있습니다.
비동기 코딩에 관한 일반적인 지식이 부족하기 때문에, 내가 사용하고있는 패턴이 실행 가능하고 b) 그것이 맞다면 어떻게 문제를 해결할 수 있을지 확신 할 수 없습니다.
조언을 주시면 감사하겠습니다.
스티븐 감사합니다. 매우 유용합니다. 뮤텍스를 구현하려했던 이유는 두 세션의 두 가지 트위터 요청을 피하기 위해서였습니다.트위터 피드는 웹 사이트의 모든 페이지에 표시되므로 피드가 반환하는 데 5 초가 걸리면 동시 요청 수가 2 회일 확률은 상당히 높지만 사용자의 입장을 고려합니다. 다시 귀하의 상세한 설명과 예제를 주셔서 감사합니다. – Neilski
작업을 캐시하면 트위터 피드가 두 번 요청 될 확률이 매우 낮습니다. 다운로드가 * 시작된 순간 * 즉시 * 작업이 캐시에 추가되기 때문입니다. –
그것은 실제로 환상적으로 영리합니다. 나는 그것이 다운로드되기 전에 다른 프로세스가 캐시에 대한 요청을하는 경우에도 Task를 캐싱하기 때문에 Task가 응답 할 때까지 기다리고 있다고 가정합니다. 그것이 가능하다는 것을 이해하기가 어렵습니다! – Neilski