如果线程花费的时间太长,如何停止线程 [英] How to stop a thread if thread takes too long

查看:147
本文介绍了如果线程花费的时间太长,如何停止线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到一种情况,我将数据导出到文件中,而我被要求做的是提供一个取消按钮,如果导出时间太长,单击该按钮将停止导出.

I have a situation that i export data to a file and what i have been asked to do is to provide a cancel button which on click will stop the export if it takes too much time to export.

我开始导出到线程文件中.而且我尝试中止按钮单击上的线程.但这行不通.

I started exporting to the file in a thread. And i try to abort the thread on the button click. But it do not work.

我在Google上进行了搜索,发现不建议使用abort().但是我还应该选择什么来实现它?

I searched on Google and i found that abort() is not recommended. But what else should I choose to achieve it?

我当前的代码是:

private void ExportButtonClick(object param)
{
    IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
    DataTable dtData = ExportHelper.ToDataTable(data);
    thread = new Thread(new ThreadStart(()=>ExportHelper.DataTableToCsv(dtData, "ExportFile.csv")));
    thread.SetApartmentState(ApartmentState.STA);
    thread.IsBackground = true;
    thread.Name = "PDF";
    thread.Start();
}

private void StopButtonClick(object param)
{
    if (thread.Name == "PDF")
    {
        thread.Interrupt();
        thread.Abort();
    }
}

推荐答案

中止线程是一个坏主意,尤其是在处理文件时.您将没有机会清理半写的文件或清理不一致的状态.

Aborting a thread is a bad idea, especially when dealing with files. You won't have a chance to clean up half-written files or clean-up inconsistent state.

这不会损害 .NET运行时,它可能会损害您自己的应用程序,例如,如果worker方法使全局状态,文件或数据库记录处于不一致的状态.

It won't harm the .NET Runtime bat it can hurt your own application eg if the worker method leaves global state, files or database records in an inconsistent state.

总是优选使用 cooperative 取消-线程定期检查诸如 CancellationToken .您不能使用布尔变量标志之类的简单变量,因为这会导致争用情况,例如,如果两个或多个线程试图同时设置它.

It's always preferable to use cooperative cancellation - the thread periodically checks a coordination construct like a ManualResetEvent or CancellationToken. You can't use a simple variable like a Boolean flag, as this can lead to race conditions, eg if two or more threads try to set it at the same time.

您可以在

You can read about cancellation in .NET in the Cancellation in Managed Threads section of MSDN.

.NET 4中添加了CancellationToken/CancellationTokenSource类,以使取消操作比传递事件更容易.

The CancellationToken/CancellationTokenSource classes were added in .NET 4 to make cancellation easier that passing around events.

在您的情况下,您应该修改DataTableToCsv以接受 CancellationToken .该令牌由 CancellationTokenSource 类.

In your case, you should modify your DataTableToCsv to accept a CancellationToken. That token is generated by a CancellationTokenSource class.

致电 CancellationTokenSource.Cancel 令牌的 IsCancellationRequested 属性变为true.您的DataTableToCsv方法应定期检查此标志.如果已设置,它将退出任何循环,删除所有不一致的文件,等等.

When you call CancellationTokenSource.Cancel the token's IsCancellationRequested property becomes true. Your DataTableToCsv method should check this flag periodically. If it's set, it should exit any loops, delete any inconsistent files etc.

Timeouts are directly supported with CancelAfter. Essentially, CancelAfter starts a timer that will fire Cancel when it expires.

您的代码可能如下所示:

Your code could look like this:

CancellationTokenSource _exportCts = null;

private void ExportButtonClick(object param)
{
    IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
    DataTable dtData = ExportHelper.ToDataTable(data);

    _exportCts=new CancellationTokenSource();
    var token=_exportCts.Token;

    thread = new Thread(new ThreadStart(()=>
            ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
    thread.SetApartmentState(ApartmentState.STA);
    thread.IsBackground = true;
    thread.Name = "PDF";

    _exportCts.CancelAfter(10000);
    thread.Start();

}


private void StopButtonClick(object param)
{
    if (_exportCts!=null)
    {
        _exportCts.Cancel();
    }
}

DataTableToCsv应包含与此类似的代码:

DataTableToCsv should contain code similar to this:

foreach(var row in myTable)
{
    if (token.IsCancellationRequested)
    {
        break;
    }
    //else continue with processing
    var line=String.Join(",", row.ItemArray);
    writer.WriteLine(line);

}

您可以使用任务而不是原始线程来清理代码:

You can clean up your code quite a bit by using tasks instead of raw threads:

private async void ExportButtonClick(object param)
{
    IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
    DataTable dtData = ExportHelper.ToDataTable(data);

    _exportCts=new CancellationTokenSource();
    var token=_exportCts.Token;

    _exportCts.CancelAfter(10000);
    await Task.Run(()=> ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
    MessageBox.Show("Finished");
}

您还可以通过使用异步操作来加快速度,例如从数据库中读取数据或写入文本文件而不会阻塞或使用线程. Windows IO(文件和网络)在驱动程序级别是异步的.像 File.WriteLineAsync 之类的方法不会'使用线程写入文件.

You could also speed it up by using asynchronous operations, eg to read data from the database or write to text files without blocking or using threads. Windows IO (both file and network) is asynchronous at the driver level. Methods like File.WriteLineAsync don't use threads to write to a file.

您的导出"按钮处理程序可能变为:

Your Export button handler could become :

private void ExportButtonClick(object param)
{
    IList<Ur1R2_Time_Points> data = ct.T_UR.ToList();
    DataTable dtData = ExportHelper.ToDataTable(data);

    _exportCts=new CancellationTokenSource();
    var token=_exportCts.Token;

    _exportCts.CancelAfter(10000);
    await Task.Run(async ()=> ExportHelper.DataTableToCsv(dtData, "ExportFile.csv",token)));
    MessageBox.Show("Finished");
}

DataTableToCsv:

public async Task DataTableToCsv(DataTable table, string file,CancellationToken token)
{
...
    foreach(var row in myTable)
    {
        if (token.IsCancellationRequested)
        {
            break;
        }
        //else continue with processing
        var line=String.Join(",", row.ItemArray);
        await writer.WriteLineAsync(line);
    }

这篇关于如果线程花费的时间太长,如何停止线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆