Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 由于挂起数据库调用而取消线程_C#_.net_Multithreading - Fatal编程技术网

C# 由于挂起数据库调用而取消线程

C# 由于挂起数据库调用而取消线程,c#,.net,multithreading,C#,.net,Multithreading,我为一个高性能、多线程邮件合并设计并制作了一个原型应用程序,作为Windows服务(C#)运行。这个问题涉及问题的一个棘手部分,即如果进程挂起数据库调用,该怎么办。我对此做了很多研究。我读过很多关于线程取消的文章,最终我只看到一种方法,thread.Abort()。是的,我知道,绝对不要使用Thread.Abort(),所以我已经研究了好几天如何用另一种方法来做,在我看来,没有其他选择。我会告诉你为什么,希望你能告诉我为什么我错了 仅供参考,这些线程是指长时间运行的线程,因此TPL无论如何都会将

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

仅供参考,这些线程是指长时间运行的线程,因此TPL无论如何都会将它们置于线程池之外

TPL只是一个很好的线程包装器,所以我认为一个任务绝对不能做线程不能做的事情。只是做得不同而已

使用线程时,有两种停止它的选择。 1.在处理循环中进行线程轮询,查看是否有标志请求取消,然后结束处理,让线程退出。没问题。 2.调用Thread.Abort()

这是线程中的数据库调用,因此一旦启动轮询,它将不起作用

另一方面,如果您使用TPL和CancellationToken,在我看来您仍然在轮询,然后创建一个异常。它看起来和我在案例1中描述的线程一样。一旦启动数据库调用(我还打算在其周围放置一个异步/等待),就无法测试CancellationToken中的更改。就这一点而言,TPL更糟糕,因为在Db读取期间调用CancellationToken完全不起任何作用,远远低于Thread.Abort()的作用

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

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

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

我会把这个链接,因为它声称这样做,但我有困难弄清楚,没有答复,使我认为它将工作

哦,这看起来是一个很好的可能性,但我也不知道

你实际上不能取消DB操作。请求通过网络发送;现在是“外面”了,不能再往后拉了。您真正能做的最好的事情就是忽略返回的响应,继续执行操作实际完成时本应执行的任何代码。认识到这是什么很重要;这实际上并没有取消任何东西,只是继续前进,即使你还没有完成。这是一个非常重要的区别

如果您有一些任务,并且希望在需要时将其取消,则可以创建一个使用
CancellationToken
的延续,以便在标记指示应该取消时将延续标记为已取消,或者在任务完成时将其完成。然后,您可以使用该延续的
任务
代替所有延续的实际基础任务,如果取消令牌,则该任务将被取消

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);
}
取消的公共静态任务(此任务
,CancellationToken(令牌)
{
返回task.ContinueWith(t=>t.GetAwaiter().GetResult(),token);
}
取消的公共静态任务(此任务
,CancellationToken(令牌)
{
返回task.ContinueWith(t=>t.GetAwaiter().GetResult(),token);
}
然后,您可以接受一个给定的任务,传入一个取消令牌,然后返回一个任务,该任务将具有相同的结果,除非更改了取消语义。

您不能实际取消DB操作。请求通过网络发送;现在是“外面”了,不能再往后拉了。您真正能做的最好的事情就是忽略返回的响应,继续执行操作实际完成时本应执行的任何代码。认识到这是什么很重要;这实际上并没有取消任何东西,只是继续前进,即使你还没有完成。这是一个非常重要的区别

如果您有一些任务,并且希望在需要时将其取消,则可以创建一个使用
CancellationToken
的延续,以便在标记指示应该取消时将延续标记为已取消,或者在任务完成时将其完成。然后,您可以使用该延续的
任务
代替所有延续的实际基础任务,如果取消令牌,则该任务将被取消

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);
}
取消的公共静态任务(此任务
,CancellationToken(令牌)
{
返回task.ContinueWith(t=>t.GetAwaiter().GetResult(),token);
}
取消的公共静态任务(此任务
,CancellationToken(令牌)
{
返回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.
}
如果操作被取消,则无需抛出异常。而且不需要
线程.Abort

使用
T,这一切都变得更加干净