首页 > 解决方案 > 执行可通过 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#multithreadingwinformspass-by-reference

解决方案


推荐阅读