由于挂起Db调用而取消线程

本文关键字:调用 取消 线程 Db 于挂起 挂起 | 更新日期: 2023-09-27 17:54:11

我设计并制作了一个原型应用程序,用于作为Windows服务(c#)运行的高性能多线程邮件合并。这个问题涉及问题的一个棘手部分,即如果进程挂起数据库调用该怎么办。我对此做了很多研究。我读了很多关于线程取消的文章,我最终只看到了一种方法:thread. abort()。是的,我知道,绝对不要使用Thread.Abort(),所以我已经研究了好几天如何以另一种方式做到这一点,正如我所看到的,没有其他选择。我会告诉你为什么,希望你能告诉我为什么我错了。

仅供参考,这些意味着长时间运行的线程,所以TPL无论如何都会将它们置于ThreadPool之外。

TPL只是一个很好的线程包装器,所以我看到绝对没有一个任务可以做线程不能做的事情。只是方法不同而已。

使用线程,您有两种选择来停止它。1. 让线程在处理循环中轮询,看看是否有标志请求取消,然后结束处理,让线程死亡。没有问题。2. 调用Thread.Abort()(然后捕获异常,执行Join并担心Finally等)

这是线程中的数据库调用,因此轮询一旦启动就不会工作。

另一方面,如果你使用TPL和CancellationToken,在我看来,你仍然在轮询,然后创建一个异常。它看起来和我在情况1中描述的线程一样。一旦启动该数据库调用(我还打算在它周围放置async/await),我就无法测试CancellationToken中的更改。对于这个问题,TPL更糟糕,因为在读取Db期间调用CancellationToken将不会做任何事情,远远小于Thread.Abort()所做的事情。

我不相信这是一个独特的问题,但我没有找到一个真正的解决方案,我读了很多。无论是线程还是任务,工作线程都必须轮询以知道它应该停止,然后停止(当连接到Db时不可能)。),否则线程必须中止,抛出ThreadAbortedException或TaskCanceledException。

我目前的计划是将每个作业作为一个长运行线程启动。如果线程超过了时间限制,我将调用thread。Abort,捕获线程中的异常,然后在Abort()之后对线程执行Join()。

我非常,非常愿意接受建议…谢谢,迈克

我会把这个链接,因为它声称这样做,但我有麻烦弄清楚,没有回复让我认为它会工作multi-threading-cross-class-cancellation-with-tpl

哦,这看起来像一个很好的可能性,但我不知道它是否将线程视为服务

由于挂起Db调用而取消线程

你不能实际上取消DB操作。请求通过网络发送;它已经"公开"了,没办法再收回了。您所能做的最好的事情就是忽略返回的响应,并继续执行在操作实际完成时应该执行的任何代码。认识到这是什么很重要;这并没有消去任何东西,只是继续前进,即使你还没做完。这是一个非常重要的区别。

如果你有一些任务,你希望它在你想要的时候被取消,你可以创建一个使用CancellationToken的延续,这样延续将在令牌指示它应该被取消时被标记为取消,或者当任务完成时它将被完成。然后,您可以使用该延续的Task来代替所有延续的实际底层任务,如果令牌被取消,任务将被取消。

public static Task WithCancellation(this Task task
    , CancellationToken token)
{
    return task.ContinueWith(t => t.GetAwaiter().GetResult(), token);
}
public static Task<T> WithCancellation<T>(this Task<T> task
    , CancellationToken token)
{
    return task.ContinueWith(t => t.GetAwaiter().GetResult(), token);
}

然后,您可以接受给定的任务,传入取消令牌,并返回具有相同结果的任务,只是取消语义发生了变化。

您还有几个其他的线程取消选项。例如,您的线程可以进行异步数据库调用,然后等待该调用和取消令牌。例如:

// cmd is a SqlCommand object
// token is a cancellation token
IAsyncResult ia = cmd.BeginExecuteNonQuery();  // starts an async request
WaitHandle[] handles = new WaitHandle[]{token.WaitHandle, ia.AsyncWaitHandle};
var ix = WaitHandle.WaitAny(handles);
if (ix == 0)
{
    // cancellation was requested
}
else if (ix == 1)
{
    // async database operation is done. Harvest the result.
}

如果操作被取消,则不需要抛出异常。不需要Thread.Abort

对于Task,这一切都变得更加清晰,但本质上是一样的。Task处理常见错误,并帮助您更好地将所有部分组合在一起。

你说

:

TPL只是一个很好的线程包装器,所以我看到绝对没有一个任务可以做线程不能做的事情。只是方法不同而已。

就目前而言,那是真的。毕竟,c#只是汇编语言程序的一个很好的包装器,所以我完全没有看到c#程序可以做汇编语言不能做的事情。但是用c#来做要容易得多,也快得多。

TPL或Tasks与管理自己的线程之间的区别也是如此。你可以管理你自己的线程,或者你可以让TPL处理所有的细节,更有可能把它做好。