中止线程是个坏主意,尤其是在处理文件时。您将没有机会清理写一半的文件或清理不一致的状态。
它不会损害 .NET 运行时 bat,它可能会损害您自己的应用程序,例如,如果工作方法使全局状态、文件或数据库记录处于不一致的状态。
最好使用 cooperative 取消 - 线程会定期检查协调构造,如 ManualResetEvent 或 CancellationToken。您不能使用像布尔标志这样的简单变量,因为这会导致竞争条件,例如如果两个或多个线程尝试同时设置它。
您可以在 MSDN 的 Cancellation in Managed Threads 部分阅读 .NET 中的取消。
在 .NET 4 中添加了 CancellationToken/CancellationTokenSource 类,使取消事件比传递事件更容易。
在您的情况下,您应该修改您的DataTableToCsv 以接受CancellationToken。该令牌由CancellationTokenSource 类生成。
当您调用 CancellationTokenSource.Cancel 时,令牌的 IsCancellationRequested 属性变为 true。您的 DataTableToCsv 方法应定期检查此标志。如果设置了,它应该退出任何循环,删除任何不一致的文件等。
CancelAfter 直接支持超时。本质上,CancelAfter 启动了一个计时器,当它到期时将触发Cancel。
您的代码可能如下所示:
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 应该包含类似这样的代码:
foreach(var row in myTable)
{
if (token.IsCancellationRequested)
{
break;
}
//else continue with processing
var line=String.Join(",", row.ItemArray);
writer.WriteLine(line);
}
你可以通过使用任务而不是原始线程来清理你的代码:
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 这样的方法不使用线程来写入文件。
您的导出按钮处理程序可能变成:
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);
}