Фоновый работник C# остановите работника - немедленно (включая Thread.Sleep в DoWork)
Я хочу просто остановить моего фонового работника, когда я нажимаю кнопку: Код выглядит так:
Кнопка:
private void button6_Click(object sender, EventArgs e)
{
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
if (isOn == true)
{
isOn = false;
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync();
this.button6.ForeColor = System.Drawing.Color.Lime;
}
}
else
{
isOn = true;
this.button6.ForeColor = System.Drawing.Color.Red;
backgroundWorker1.CancelAsync();
//////backgroundWorker1.Dispose();
}
А мой Backgroundworker_DoWork выглядит так:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (backgroundWorker1.CancellationPending && backgroundWorker1.IsBusy)
{
e.Cancel = true;
return;
}
while (true)
{
if (backgroundWorker1.CancellationPending && backgroundWorker1.IsBusy)
{
e.Cancel = true;
break;
}
backgroundWorker1.Dispose();
click_na_default(hwnd1);
click_F8(hwnd1);
click_na_YELLS(hwnd1);
click_ENTER(hwnd1);
Thread.Sleep(100);
click_na_trade(hwnd1);
Thread.Sleep(100);
click_F8(hwnd1);
click_ENTER(hwnd1);
Thread.Sleep(100);
click_na_default(hwnd1);
Thread.Sleep(4000);
}
if (((BackgroundWorker)sender).CancellationPending)
{
e.Cancel = true;
//set this code at the end of file processing
return;
}
}
И проблема в том, что я не могу .CancelAsync();
только сразу после нажатия кнопки снова. Мой код просто DoWork до просто Thread.Sleep(4000)
; кончено. Когда я нажму кнопку, чтобы остановить работу, это остановится сразу после завершения цикла.
Я знаю, что могу добавить
if (backgroundWorker1.CancellationPending && backgroundWorker1.IsBusy)
{
e.Cancel = true;
return;
}
После каждой строки в моем Backgroundworker_DoWork
но это так глупо, и когда я получаю Thread.Sleep(10000)
; это займет 10 секунд... Есть ли способ просто убить мгновенно моего второстепенного работника? Спасибо за помощь!
1 ответ
Я думаю, что стандарт BackgroundWorker
не подходит для вашего случая, и вы должны сделать что-то нестандартное, чтобы лучше поддерживать сочетание сна и отмены. Следующий код представляет собой идею того, что вы можете сделать:
CancellableBackgroundWorker.cs
Этот класс похож на стандарт BackgroundWorker
но предоставляя некоторые обратные вызовы для вашей цели (см. ICancellationProvider
а также FinishedEvent
).
public delegate void CancellableBackgroundJob(ICancellationProvider cancellation);
public interface ICancellationProvider
{
bool CheckForCancel();
void CheckForCancelAndBreak();
void SleepWithCancel(int millis);
}
public class CancellableBackgroundWorker : Component, ICancellationProvider
{
private readonly ManualResetEvent _canceledEvent = new ManualResetEvent(false);
private readonly CancellableBackgroundJob _backgroundJob;
private volatile Thread _thread;
private volatile bool _disposed;
public EventHandler FinishedEvent;
public CancellableBackgroundWorker(CancellableBackgroundJob backgroundJob)
{
_backgroundJob = backgroundJob;
}
protected override void Dispose(bool disposing)
{
Cancel();
_disposed = true;
}
private void AssertNotDisposed()
{
if (_disposed)
throw new InvalidOperationException("Worker is already disposed");
}
public bool IsBusy
{
get { return (_thread != null); }
}
public void Start()
{
AssertNotDisposed();
if (_thread != null)
throw new InvalidOperationException("Worker is already started");
_thread = new Thread(DoWorkWrapper);
_thread.Start();
}
public void Cancel()
{
AssertNotDisposed();
_canceledEvent.Set();
}
private void DoWorkWrapper()
{
_canceledEvent.Reset();
try
{
_backgroundJob(this);
Debug.WriteLine("Worker thread completed successfully");
}
catch (ThreadAbortException ex)
{
Debug.WriteLine("Worker thread was aborted");
Thread.ResetAbort();
}
finally
{
_canceledEvent.Reset();
_thread = null;
EventHandler finished = FinishedEvent;
if (finished != null)
finished(this, EventArgs.Empty);
}
}
#region ICancellationProvider
// use explicit implementation of the interface to separate interfaces
// I'm too lazy to create additional class
bool ICancellationProvider.CheckForCancel()
{
return _canceledEvent.WaitOne(0);
}
void ICancellationProvider.CheckForCancelAndBreak()
{
if (((ICancellationProvider)this).CheckForCancel())
{
Debug.WriteLine("Cancel event is set, aborting the worker thread");
_thread.Abort();
}
}
void ICancellationProvider.SleepWithCancel(int millis)
{
if (_canceledEvent.WaitOne(millis))
{
Debug.WriteLine("Sleep aborted by cancel event, aborting the worker thread");
_thread.Abort();
}
}
#endregion
}
Основная хитрость заключается в использовании ManualResetEvent.WaitOne
вместо Thread.Sleep
для сна. При таком подходе рабочий поток может быть безопасно выведен (для отмены) из другого потока (пользовательского интерфейса). Еще один трюк заключается в использовании ThreadAbortException
с помощью Thread.Abort
обеспечить быстрое завершение фонового выполнения потока (и не забывайте о Thread.ResetAbort
в конце стека раскручивается).
Вы можете использовать этот класс следующим образом:
public partial class Form1 : Form
{
private readonly CancellableBackgroundWorker _backgroundWorker;
public Form1()
{
InitializeComponent();
_backgroundWorker = new CancellableBackgroundWorker(DoBackgroundJob);
_backgroundWorker.FinishedEvent += (s, e) => UpdateButton();
// ensure this.components is created either by InitializeComponent or by us explicitly
// so we can add _backgroundWorker to it for disposal
if (this.components == null)
this.components = new System.ComponentModel.Container();
components.Add(_backgroundWorker);
}
private void UpdateButton()
{
// Ensure we interact with UI on the main thread
if (InvokeRequired)
{
Invoke((Action)UpdateButton);
return;
}
button1.Text = _backgroundWorker.IsBusy ? "Cancel" : "Start";
}
private void button1_Click(object sender, EventArgs e)
{
if (_backgroundWorker.IsBusy)
{
_backgroundWorker.Cancel();
}
else
{
_backgroundWorker.Start();
}
UpdateButton();
}
private void DoBackgroundJob(ICancellationProvider cancellation)
{
Debug.WriteLine("Do something");
// if canceled, stop immediately
cancellation.CheckForCancelAndBreak();
Debug.WriteLine("Do something more");
if (cancellation.CheckForCancel())
{
// you noticed cancellation but still need to finish something
Debug.WriteLine("Do some necessary clean up");
return;
}
// Sleep but cancel will stop and break
cancellation.SleepWithCancel(10000);
Debug.WriteLine("Last bit of work");
}
}