Фон
В моей библиотеке утилит (Shd.dll) у меня есть класс AsyncOperation.Проще говоря, это базовый класс для типов, которые инкапсулируют потенциально длительную операцию, выполняют ее в фоновом потоке и поддерживают паузу / возобновление, отмену и отчеты о ходе выполнения.(Это похоже на BackgroundWorker, просто он знает больше вещей.)
В коде пользователя вы можете использовать его так:
class MyOperation : AsyncOperation
{
public MyOperation() : base(null, AsyncOperationOptions.Cancelable | AsyncOperationOptions.Pausable) {}
protected override void RunOperation(AsyncOperationState operationState, object userState)
{
...
operationState.ThrowIfCancelled();
}
}
var op = new MyOperation();
op.Start();
...
op.Cancel();
operationState.ThrowIfCancelled () делает именно то, что предполагает его имя:если метод Cancel () был вызван ранее другим потоком, он генерирует внутреннее исключение (AsyncOperationCancelException), которое затем обрабатывается типом AsyncOperation, например так:
private void _DoExecute(object state)
{
// note that this method is already executed on the background thread
...
try
{
operationDelegate.DynamicInvoke(args); // this is where RunOperation() is called
}
catch(System.Reflection.TargetInvocationException tiex)
{
Exception inner = tiex.InnerException;
var cancelException = inner as AsyncOperationCancelException;
if(cancelException != null)
{
// the operation was cancelled
...
}
else
{
// the operation faulted
...
}
...
}
...
}
Это работает отлично.Или так я думал в прошлом году, когда использовал это в многочисленных сценариях.
Актуальная проблема
Я создаю класс, который использует System.Net.WebClient для загрузки потенциально больших файлов.количество файлов через FTP.Этот класс построен с использованием базового класса AsyncOperation, как описано выше.
Для точных отчетов о ходе работы я использую WebClient.UploadFileAsync (), что усложняет код, но соответствующие части выглядят так:
private ManualResetEventSlim completedEvent = new ManualResetEventSlim(false);
private void WebClient_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
{
...
if (OperationState.IsCancellationRequested)
{
_GetCurrentWebClient().CancelAsync();
}
}
private void WebClient_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
{
...
_UploadNextFile();
}
private void _UploadNextFile()
{
if (OperationState.IsCancellationRequested || ...)
{
this.completedEvent.Set();
return;
}
...
}
protected override void RunOperation(AsyncOperationState operationState, object userState)
{
...
_UploadNextFile();
this.completedEvent.Wait();
operationState.ThrowIfCancelled(); // crash
...
}
Как видите, я отметил линию, где происходит сбой.Что именно происходит, так это то, что когда выполнение попадает в эту строку (я ставлю точку останова прямо над ней, поэтому я знаю, что это точная строка), Visual Studio 2010 останавливается примерно на 15 секунд, а затем следующее, что я вижу, - это исходный кодof AsyncOperationState.ThrowIfCancelled ():
public void ThrowIfCancelled()
{
if(IsCancellationRequested)
{
throw new AsyncOperationCancelException();
}
} // this is the line the debugger highlights: "An exception of type AsyncOperationCancelException' occured in Shd.dll but was unhandled by user code."
Я пытался поместить точки останова туда, где должно было быть поймано исключение, но выполнение никогда не достигает этого блока catch {}.
Другой странный этов конце он также пишет следующее: «Оценка функции отключена, потому что истек срок ожидания предыдущей оценки функции».Я погуглил эту проблему и попробовал все, что было предложено (отключил неявную оценку свойства, удалил все точки останова), но пока ничего не помогло.
Вот два скриншота, иллюстрирующих проблему:
http://dl.dropbox.com/u/17147594/vsd1.png
http://dl.dropbox.com/u/17147594/vsd2.png
Я использую .NET 4.0.Буду очень признателен за любую помощь.