0

如果后台工作人员当前正在运行,我正在尝试取消它,然后再启动另一个。

我首先尝试了这个,函数中有更多的取消检查......

    private void StartWorker()
    {
        if (StartServerGetIP.IsBusy) { StartServerGetIP.CancelAsync(); }
        StartServerGetIP.RunWorkerAsync();
    }
 private void StartServerGetIP_DoWork(object sender, DoWorkEventArgs e)
    {
        StartFTPServer(Port, Ringbuf, sender as BackgroundWorker, e);
        if ((sender as BackgroundWorker).CancellationPending) return;
        GetIP(Ringbuf, sender as BackgroundWorker, e);
    }

private void StartServerGetIP_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            return;
        }
        if (e.Result.ToString() == "Faulted")
        {
            tcs.SetResult(false);
            return;
        }

        Client.IPAddress = e.Result.ToString();
        tcs.SetResult(true);
    }

如果工人在StartServerGetIP.RunWorkerAsync();

在此之后,我发现了一个丑陋的解决方案

        private void StartWorker()
    {
        if (StartServerGetIP.IsBusy) { StartServerGetIP.CancelAsync(); }
        while(StartServerGetIP.IsBusy) { Application.DoEvents(); }

        StartServerGetIP.RunWorkerAsync();
    }

有没有一种我可以实现的模式允许我异步取消后台工作程序并在不调用的情况下启动另一个Application.DoEvents

编辑:取消按钮是不可能的。

编辑:对于那些询问内部方法的人......

private void StartFTPServer(SerialPort port, RingBuffer<string> buffer, BackgroundWorker sender, DoWorkEventArgs args)
    {
        Stopwatch timeout = new Stopwatch();
        TimeSpan max = TimeSpan.FromSeconds(MaxTime_StartServer);
        int time_before = 0;
        timeout.Start();
        while (!buffer.Return.Contains("Run into Binary Data Comm mode...") && timeout.Elapsed.Seconds < max.Seconds)
        {
            if (timeout.Elapsed.Seconds > time_before)
            {
                time_before = timeout.Elapsed.Seconds;
                sender.ReportProgress(CalcPercentage(max.Seconds, timeout.Elapsed.Seconds));
            }
            if (sender.CancellationPending)
            {
                args.Cancel = true;
                return;
            }
        }
        port.Write("q"); //gets into menu
        port.Write("F"); //starts FTP server
    }

        private void GetIP(RingBuffer<string> buffer, BackgroundWorker sender, DoWorkEventArgs args)
    {
        //if longer than 5 seconds, cancel this step
        Stopwatch timeout = new Stopwatch();
        TimeSpan max = TimeSpan.FromSeconds(MaxTime_GetIP);
        timeout.Start();
        int time_before = 0;
        string message;

        while (!(message = buffer.Return).Contains("Board IP:"))
        {
            if (timeout.Elapsed.Seconds > time_before)
            {
                time_before = timeout.Elapsed.Seconds;
                sender.ReportProgress(CalcPercentage(max.Seconds, timeout.Elapsed.Seconds + MaxTime_StartServer));
            }
            if (timeout.Elapsed.Seconds >= max.Seconds)
            {
                args.Result = "Faulted";
                return;
            }
            if (sender.CancellationPending)
            {
                args.Cancel = true;
                return;
            }
        }
        Regex regex = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b");
        string IP = message.Remove(0, "Board IP: ".Length);
        if (regex.IsMatch(IP))
        {
            args.Result = IP;
            ServerAlive = true;
        }
    }

还不如给你环形缓冲区..

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FPGAProgrammerLib
{
    class RingBuffer<T>
    {
        T [] buffer { get; set; }

        int _index;
        int index
        {
            get
            {
                return _index;
            }
            set
            {
                _index = (value) % buffer.Length;
            }
        }


        public T Add
        {
            set
            {
                buffer[index++] = value;
            }
        }

        public T Return
        {
            get
            {
                return (index == 0) ? (IsString() ? (T)(object)string.Empty : default(T)) : buffer[--index];
            }
        }

        private bool IsString()
        {
            return (typeof(T) == typeof(string) || (typeof(T) == typeof(String)));
        }

        public RingBuffer(int size)
        {
            buffer = new T[size];
            index = 0;
        }
    }
}
4

2 回答 2

0

这是使用 Microsoft 的 Reactive Framework (Rx) 有效取消在另一个线程上运行的运行中函数的简单方法。

从返回所需值的长时间运行函数开始:

Func<string> GetIP = () => ...;

你有某种触发器——可能是按钮点击或计时器等——或者在我的情况下,我使用的是 Rx 库中的类型。

Subject<Unit> trigger = new Subject<Unit>();

然后你可以写这段代码:

IObservable<string> query =
    trigger
        .Select(_ => Observable.Start(() => GetIP()))
        .Switch()
        .ObserveOn(this);

IDisposable subscription =
    query
        .Subscribe(ip => { /* Do something with `ip` */ });

现在,只要我想启动该功能,我就可以调用它:

trigger.OnNext(Unit.Default);

如果我在现有呼叫正在运行时发起新呼叫,则现有呼叫将被忽略,并且只有最新呼叫(只要它完成)最终会由查询生成,并且订阅将获得它。

查询继续运行并响应每个触发事件。

.ObserveOn(this)(假设您使用的是 WinForms)将结果返回到 UI 线程。

只需 NuGet "System.Reactive.Windows.Forms" 即可获取这些信息。

如果你想trigger成为一个按钮点击,这样做:

IObservable<Unit> trigger =
    Observable
        .FromEventPattern<EventHandler, EventArgs>(
            h => button.Click += h,
            h => button.Click -= h)
        .Select(_ => Unit.Default);
于 2017-10-28T03:27:37.250 回答
0

在您的 StartServerGetIP_DoWork 方法中有一个 StartFTPServer 方法。如果已请求取消,我假设您不会签入该方法。这同样适用于您的 GetIP 方法。这些可能是您的障碍点。如果要确保实际取消作业,则需要定期检查是否已请求取消。因此,我建议您对 StartFTPServer 和 GetIP 使用异步方法,以检查后台工作人员是否请求取消。

我不知道您在 StartFTPServer 方法或 GetIP 方法中所做的确切实现。如果您想了解有关如何重构代码的更多详细信息,以便可以在发布代码后取消它。

于 2017-10-27T22:53:49.130 回答