우리는 생산에서 다음과 같은 코드를 많이 사용하고 있습니다 .
var result = WaitFor<Result>.Run(1.Minutes(), () => service.GetSomeFragileResult());
구현은 오픈 소스이며 병렬 컴퓨팅 시나리오에서도 효율적으로 작동하며 Lokad 공유 라이브러리 의 일부로 제공됩니다.
[Immutable]
public sealed class WaitFor<TResult>
{
readonly TimeSpan _timeout;
public WaitFor(TimeSpan timeout)
{
_timeout = timeout;
}
public TResult Run(Func<TResult> function)
{
if (function == null) throw new ArgumentNullException("function");
var sync = new object();
var isCompleted = false;
WaitCallback watcher = obj =>
{
var watchedThread = obj as Thread;
lock (sync)
{
if (!isCompleted)
{
Monitor.Wait(sync, _timeout);
}
}
if (!isCompleted)
{
watchedThread.Abort();
}
};
try
{
ThreadPool.QueueUserWorkItem(watcher, Thread.CurrentThread);
return function();
}
catch (ThreadAbortException)
{
Thread.ResetAbort();
throw new TimeoutException(string.Format("The operation has timed out after {0}.", _timeout));
}
finally
{
lock (sync)
{
isCompleted = true;
Monitor.Pulse(sync);
}
}
}
public static TResult Run(TimeSpan timeout, Func<TResult> function)
{
return new WaitFor<TResult>(timeout).Run(function);
}
}
이 코드는 여전히 버그가 있습니다.이 작은 테스트 프로그램으로 시도해 볼 수 있습니다.
static void Main(string[] args) {
var sb = new StringBuilder();
for (var j = 1; j < 10; j++)
for (var ii = 8; ii < 15; ii++) {
int i = ii;
try {
Debug.WriteLine(i);
try {
WaitFor<int>.Run(TimeSpan.FromMilliseconds(10), () => {
Thread.Sleep(i);
sb.Append("Processed " + i + "\r\n");
return i;
});
}
catch (TimeoutException) {
sb.Append("Time out for " + i + "\r\n");
}
Thread.Sleep(10);
}
catch (ThreadAbortException) {
Thread.ResetAbort();
sb.Append(" *** ThreadAbortException on " + i + " *** \r\n");
}
}
Console.WriteLine(sb.ToString());
}
}
경쟁 조건이 있습니다. 메서드 WaitFor<int>.Run()
가 호출 된 후에 ThreadAbortException이 발생할 가능성이 분명합니다 . 나는 이것을 고칠 믿을만한 방법을 찾지 못했지만 동일한 테스트로 TheSoftwareJedi가 받아 들인 대답으로 문제를 재현 할 수 없습니다 .