- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我遇到一种情况,我将数据导出到文件,我被要求做的是提供一个取消按钮,如果导出时间过长,单击该按钮将停止导出。
我开始在线程中导出到文件。我尝试在单击按钮时中止线程。但这不起作用。
我在Google上搜索了一下,发现不推荐使用abort()。但我还应该选择什么来实现它?
我当前的代码是:
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();
}
}
最佳答案
中止线程是一个坏主意,尤其是在处理文件时。您将没有机会清理半写入的文件或清理不一致的状态。
它不会损害 .NET 运行时,但它可能会损害您自己的应用程序,例如,如果辅助方法使全局状态、文件或数据库记录处于不一致的状态。
最好使用协作取消 - 线程定期检查协调构造,如 ManualResetEvent或CancellationToken 。您不能使用像 bool 标志这样的简单变量,因为这可能会导致竞争条件,例如,如果两个或多个线程尝试同时设置它。
您可以在 Cancellation in Managed Threads 中阅读有关 .NET 中的取消的信息。 MSDN 部分。
.NET 4 中添加了 CancellationToken/CancellationTokenSource 类,以使取消比传递事件更容易。
就您而言,您应该修改您的 DataTableToCsv
以接受 CancellationToken 。该 token 是由 CancellationTokenSource 生成的类(class)。
当您调用CancellationTokenSource.Cancel时 token 的 IsCancellationRequested属性变为真。您的 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);
}
关于c# - 如果线程花费太长时间,如何停止线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42268735/
在我们的服务出现一些预期的增长之后,突然间一些更新花费了非常长的时间,这些过去非常快,直到表达到大约 2MM 记录,现在它们每个需要大约 40-60 秒。 update table1 set fiel
我在服务中实现了一个传感器事件监听器,只要采样周期和最大报告延迟低于 1 秒,该监听器就可以正常工作,但一旦我将采样周期增加到超过 1 秒,传感器就根本不会更新。 我希望采样周期为 10 秒(可能是
我使用 Tkinter GUI 来启动测量和分析过程,基本上只需单击一个按钮即可开始。由于这些测量可能需要一段时间,我尝试添加一个进度条,即这个: http://tkinter.unpythonic.
我正在尝试使用套接字发送数据包,但出现错误。 invalid conversion from ‘omnetpp::cPacket*’ to ‘inet::Packet*’ [-fpermissive]
我刚刚发现 String#split 有以下奇怪的行为: "a\tb c\nd".split => ["a", "b", "c", "d"] "a\tb c\nd".split(' ') => ["a
您好,我正在尝试 ClojureScript,我正在使用 Klipse作为我的 REPL 差不多。这可能不是它的预期用途,但因为我没有做任何太复杂的事情,所以现在没问题。 我遇到的一个问题是尝试设置计
根据下面的数据,ClockKit 会生成一次 future 的 CLKComplicationTimelineEntry 项,但对于过去的时间点,会进行 24 次调用!这是为什么? 更多详情: 我注意
我有一个 MySQL 表,这个表有一个名为 datetime_utc 的 DATETIME 列。如您所料,它是 UTC 日期和时间。在我的 Bookshelf 模型中,我定义了一个虚拟 getter,
大家好,我是二哥呀! 昨天,一位球友问我能不能给他解释一下 @SpringBootApplication 注解是什么意思,还有 Spring Boot 的运行原理,于是我就带着他扒拉了一下这个注解的源
我是一名优秀的程序员,十分优秀!