我正在尝试取消后台工作程序,如果它当前正在运行,然后再启动另一个。
我首先尝试了这个,在函数中有更多的取消检查......
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;
}
}
}
答案 0 :(得分:0)
在StartServerGetIP_DoWork方法中有一个StartFTPServer方法。我假设如果要求取消,您不会检查该方法。同样的事情适用于您的GetIP方法。那些可能是你的阻挡点。如果您想确保实际取消作业,您需要定期检查是否已请求取消。所以我建议您使用StartFTPServer和GetIP的异步方法来检查后台工作者是否有请求取消。
我不知道您在StartFTPServer方法或GetIP方法中所做的确切实现。如果您想了解有关如何重构代码的更多详细信息,以便可以在代码后取消它。
答案 1 :(得分: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);