У меня есть асинхронная операция, зависящая от другого сервера, для завершения которой требуется в основном случайное количество времени. Пока выполняется асинхронная операция, в «основном потоке» также происходит обработка, которая также занимает случайное количество времени для завершения.
Основной поток запускает асинхронную задачу, выполняет свою основную задачу и в конце проверяет результат асинхронной задачи.
Асинхронный поток извлекает данные и вычисляет поля, которые не являются критическими для завершения основного потока. Однако было бы неплохо иметь эти данные (и их следует включать), если вычисление может завершиться без замедления основного потока.
Я хотел бы настроить асинхронную задачу так, чтобы она выполнялась как минимум в течение 2 секунд, но занимала все время, доступное между началом и окончанием основной задачи. Это «ленивый тайм-аут» в том смысле, что он истекает только в том случае, если превышено 2-секундное время выполнения, и результат действительно запрашивается. (Асинхронная задача должна занимать больше 2 секунд или общее время выполнения основной задачи)
EDIT (попытка уточнить требования): Если асинхронная задача была запущена в течение 2 секунд, она вообще не должна блокировать основной поток. Основной поток должен разрешить выполнение асинхронной задачи не менее 2 секунд. Кроме того, если выполнение основного потока занимает более 2 секунд, асинхронная задача должна выполняться столько же, сколько и основной поток.
Я разработал оболочку, которая работает, однако я бы предпочел решение, которое на самом деле относится к типу Task. См. мое решение для оболочки ниже.
public class LazyTimeoutTaskWrapper<tResult>
{
private int _timeout;
private DateTime _startTime;
private Task<tResult> _task;
private IEnumerable<Action> _timeoutActions;
public LazyTimeoutTaskWrapper(Task<tResult> theTask, int timeoutInMillis, System.DateTime whenStarted, IEnumerable<Action> onTimeouts)
{
this._task = theTask;
this._timeout = timeoutInMillis;
this._startTime = whenStarted;
this._timeoutActions = onTimeouts;
}
private void onTimeout()
{
foreach (var timeoutAction in _timeoutActions)
{
timeoutAction();
}
}
public tResult Result
{
get
{
var dif = this._timeout - (int)System.DateTime.Now.Subtract(this._startTime).TotalMilliseconds;
if (_task.IsCompleted ||
(dif > 0 && _task.Wait(dif)))
{
return _task.Result;
}
else
{
onTimeout();
throw new TimeoutException("Timeout Waiting For Task To Complete");
}
}
}
public LazyTimeoutTaskWrapper<tNewResult> ContinueWith<tNewResult>(Func<Task<tResult>, tNewResult> continuation, params Action[] onTimeouts)
{
var result = new LazyTimeoutTaskWrapper<tNewResult>(this._task.ContinueWith(continuation), this._timeout, this._startTime, this._timeoutActions.Concat(onTimeouts));
result._startTime = this._startTime;
return result;
}
}
У кого-нибудь есть лучшее решение, чем эта оболочка?