c# - 执行可通过 Windows 窗体按钮取消的后台进程
问题描述
我需要启动一个后台进程 (Solver),它从我的主窗体 (Form1) 接收数据并以新窗体 (FormStatus) 在 ProgressBar 中指示其进度。我决定从 Form1 启动 FormStatus,然后启动 FormStatus 的 OnLoad 以在单独的线程中执行 Solver 进程。在求解器执行的任何时候,我都可以使用 FormStatus 中的 ButtonStop 中止它。如果直接启动,该进程会阻止表单。
如果使用按钮终止进程,则抛出异常,否则(如果计算完成)Solver 返回空字符串并关闭 FormStatus。
主要 Form1 的代码:
using System;
using System.Windows.Forms;
namespace CS_Threads
{
public partial class Form1 : Form
{
// Contains some data
public class CData
{ public int Int { set; get; } }
private CData data;
public Form1()
{
InitializeComponent();
data = new CData() { Int = 100 };
}
// On ButtonGO press open FormStatus to start the solver
private void ButtonGO_Click(object sender, EventArgs e)
{
int result;
using (FormStatus f = new FormStatus(data))
{
f.ShowDialog();
result = f.Result;
}
// Fill the RTBox according to calculation results
if (result < 0) RTBox.Text = "Failed";
else RTBox.Text = "Success";
}
}
}
FormStatus 的代码:
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CS_Threads
{
public partial class FormStatus : Form
{
private Form1.CData data;
private CancellationTokenSource source;
public int Result { private set; get; }
public FormStatus(Form1.CData d)
{
InitializeComponent();
data = d; // Didn't find a better way to pass the class reference. Maybe make this member public in Form1?
Result = -1; // Default value of Result if form was closed or process terminated
source = new CancellationTokenSource();
}
// On ButtonStop press stop the Solver and close form
private void ButtonStop_Click(object sender, EventArgs e)
{
source.Cancel();
Close();
}
// On form load launch Solver: Warning CS1998 This async method lacks 'await' operators and will run synchronously.Consider using the 'await' operator...
private async void FormStatus_Load(object sender, EventArgs e)
{
Task<string> t = Task.Run(() => Solver(data, this, source.Token));
string res = t.Result;
}
// Solver: Same warning CS1998
private static async Task<string> Solver(Form1.CData data, FormStatus f, CancellationToken ct)
{
try
{
// Do some calculations
long max = 1000;
for (long i = 0; i < max && !ct.IsCancellationRequested; i++)
for (long j = 0; j < max && !ct.IsCancellationRequested; j++)
{
data.Int = 10;
f.ProgressBar.Value = (int)(((double)i / (double)max) * ((double)j / (double)max) * 100.0);
}
if (!ct.IsCancellationRequested)
{
f.Result = 0;
f.Close();
}
else throw new Exception("Task has been cancelled");
}
catch (Exception ex)
{ return ex.ToString(); }
return null;
}
}
}
现在我在 FormStatus 中有两个警告,并且进程在 Solver 的第一次迭代时终止,在“字符串 res”中引发异常:System.InvalidOperationException:跨线程操作无效:控制“ProgressBar”从线程以外的线程访问它创建于。
我刚刚开始使用线程,所以可能会有一些非常愚蠢的东西......
解决方案
推荐阅读
- c# - 返回的方法与返回变量的结果不同
- javascript - 如何从上传的图像中获取日期?
- javascript - array.map 后未定义的值 - react.js
- javascript - 如何在 javascript 中将用户输入下载为文件?
- c# - 为什么我得到 ObjectReference 空错误,而参数有一个值?
- java - iText - ExceptionConverter: java.io.IOException: Stream Closed
- c++ - ADL 是调用朋友内联函数的唯一方法吗?
- python - 如何在 Django 网页的表格中显示我的 python 代码?
- flutter - 使用不包含 Scaffold 的上下文调用 Scaffold.of()
- c# - 在我的表日期订单以错误的方式插入