线程/任务和异步处理新手... 我有一个尝试获取文件的进程,但是,如果该文件受密码保护,则对
GetDocument
的调用永远不会返回,并挂起服务。public class ServerClass
{
public static PageData pageData;
public static ImageDataProvider idp;
public static Rendition rend;
public static void AcquireRendition(object obj)
{
CancellationToken ct = (CancellationToken)obj;
while ((!ct.IsCancellationRequested) || (pageData == null))
{
pageData = idp.GetDocument(rend); ////line failing to return
}
}
}
您确定没有允许您传递取消令牌的 API 吗?您可以使用其他客户端/库吗?通常没有安全的方法来阻止“挂起”的呼叫。即使使用“同步”阻塞 I/O 方法也是一个非常糟糕的主意。然后,通过将整个对象作为静态字段来访问,而不考虑共享(ImageDataProvider
是线程安全的吗?),情况会变得更糟。
终止。 任务绝对不能被终止(它们完全依赖于合作取消),并且线程非常不安全且不可靠,会出现“粗鲁”的中止(再次强调,合作取消是首选)。您不知道会导致什么样的损坏,并且编写可以很好地处理异步异常的代码几乎是不可能的。你当然不能指望一个甚至不提供合作取消机会的图书馆。
方法中断可能卡住的线程。下面是一个辅助方法
RunInterruptible
,它观察 CancellationToken
,在令牌被取消时中断当前线程,并传播 OperationCanceledException
。它与新 API 具有相同的签名 ControlledExecution.Run
(.NET 7,源代码):
public static void RunInterruptible(Action action,
CancellationToken cancellationToken)
{
if (action == null) throw new ArgumentNullException("action");
cancellationToken.ThrowIfCancellationRequested();
bool completedSuccessfully = false;
try
{
try
{
using (CancellationTokenRegistration _ = cancellationToken
.Register(arg => ((Thread)arg).Interrupt(), Thread.CurrentThread))
action();
completedSuccessfully = true;
}
finally { Thread.Sleep(0); } // Last chance to be interrupted
}
catch (ThreadInterruptedException)
{
if (completedSuccessfully) return;
cancellationToken.ThrowIfCancellationRequested();
throw;
}
}
使用示例:
RunInterruptible(() => pageData = idp.GetDocument(rend), ct);
如果线程没有陷入等待状态,而是不受控制地旋转,则
Thread.Interrupt
将不起作用。在这种情况下,您可以尝试使用下面的
RunAbortable
方法。在采取这一严厉措施之前,请确保您充分了解在生产环境中使用
Thread.Abort
方法的影响。
// .NET Framework only
[Obsolete("The RunAbortable method may prevent the execution of static" +
" constructors and the release of managed or unmanaged resources," +
" and may leave the application in an invalid state.")]
public static void RunAbortable(Action action,
CancellationToken cancellationToken)
{
if (action == null) throw new ArgumentNullException("action");
cancellationToken.ThrowIfCancellationRequested();
bool completedSuccessfully = false;
try
{
try
{
using (CancellationTokenRegistration _ = cancellationToken
.Register(arg => ((Thread)arg).Abort(), Thread.CurrentThread))
action();
completedSuccessfully = true;
}
finally { Thread.Sleep(0); } // Last chance to be aborted
}
catch (ThreadAbortException)
{
if (completedSuccessfully)
{
Thread.ResetAbort();
return;
}
if (cancellationToken.IsCancellationRequested)
{
Thread.ResetAbort();
throw new OperationCanceledException(cancellationToken);
}
throw; // Redundant, the ThreadAbortException is rethrown anyway.
}
}