[英]Cancelling a BackgroundWorker
我的DoWork
for backgroundworker1
通過WakeUp
類設置了一個等待計時器,它運行良好。
現在的問題是,有時我的while CancellationPending
會以無限循環結束。 因此,在DoWork
內部有一個對WaitOne
, DoWork
設置了等待計時器的內容並等待線程直到計時器觸發。
我需要立即關閉BackgroundWorker
,但同時也必須保留對BackgroundWorker
的引用,以便可以跟蹤程序中的每個警報。 為什么CancellationPending
需要這么長時間? 它似乎從未完成。 有什么方法可以殺死backgroundworker
而不必在while循環中等待這么長時間?
switch (alarmNum)
{
case 1:
WakeUp.CancelWakeUp(threadHandles[removal]);
if(backgroundworker1.IsBusy)
{
backgroundworker1.CancelAsync();
}
while(backgroundworker1.CancellationPending)
{
}
backgroundworker1.RunWorkerAsync();
break;
case 2:
if (backgroundworker2.IsBusy)
{
backgroundworker2.CancelAsync();
}
backgroundworker2.RunWorkerAsync();
break;
case 3:
if (backgroundworker3.IsBusy)
{
backgroundworker3.CancelAsync();
}
backgroundworker3.RunWorkerAsync();
break;
case 4:
if (backgroundworker4.IsBusy)
{
backgroundworker4.CancelAsync();
}
backgroundworker4.RunWorkerAsync();
break;
case 5:
if (backgroundworker5.IsBusy)
{
backgroundworker5.CancelAsync();
}
backgroundworker5.RunWorkerAsync();
break;
}
private void bw1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (worker.CancellationPending == true)
{
e.Cancel = true;
}
WakeUp temp = new WakeUp("spalarm1");
threadHandles[0] = temp.tHandle;
temp.initWakeUp(dtCurSpan);
//****************************
It's blocking here -< <--
temp.DoWork sets a system timer so its blocking
with a call to WaitOne inside WakeUp. The timer I'm setting
is called a waitable timer so its blocking since the system is
waiting for it to expire so the thread can end
*******************************/
temp.DoWork();
}
WakeUp.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Diagnostics;
namespace WpfApplication1
{
class WakeUp
{
public delegate void TimerCompleteDelegate(IntPtr complretionArg,
UInt32 timerLow, UInt32 timerHigh);
public SafeWaitHandle tHandle;
bool rslt;
//Various imports of kernel32.dll so the waitable timer can be set
//on the system
[DllImport("kernel32.dll")]
public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer, [In] ref long pDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, bool fResume);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CancelWaitableTimer(SafeWaitHandle hTimer);
//SafeHandle.DangerousGetHandle Method
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
//The constructor will use a TimeSpan to set a waitable timer
public WakeUp(string wtName)
{
tHandle = CreateWaitableTimer(IntPtr.Zero, true, wtName);
}
public int initWakeUp(TimeSpan smParam)
{
//The number of ticks needs to be negated to set a waitable timer in this fashion
long waketicks = -smParam.Ticks;
rslt = SetWaitableTimer(tHandle, ref waketicks, 0, IntPtr.Zero, IntPtr.Zero, true);
if(!rslt)
{
return Marshal.GetLastWin32Error();
}
else
{
return 0;
}
}
private static Exception GetWin32Exception()
{
return Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error());
}
public int DoWork()
{
using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset))
{
wh.SafeWaitHandle = tHandle;
wh.WaitOne();
Thread.Sleep(5000);
}
return 0;
}
//This function needs to check the return value of
//CancelWaitableTimer
static public void CancelWakeUp(SafeWaitHandle clHandle)
{
CancelWaitableTimer(clHandle);
}
}
}
可能的解決方案:
case 1:
WakeUp.CancelWakeUp(threadHandles[removal]);
threadHandles[removal] = null;
backgroundworker1.CancelAsync();
backgroundworker1.Dispose();
backgroundworker1 = new BackgroundWorker();
backgroundworker1.WorkerReportsProgress = true;
backgroundworker1.WorkerSupportsCancellation = true;
backgroundworker1.DoWork += new DoWorkEventHandler(bw1_DoWork);
backgroundworker1.ProgressChanged += new ProgressChangedEventHandler(bw1_ProgressChanged);
backgroundworker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw1_RunWorkerCompleted);
backgroundworker1.RunWorkerAsync();
break;
但是我試圖避免這種類型的代碼。
我不熟悉您正在PInvoking的kernel32 API,因此我將為您提供一個使用System.Threading.Timer
的基本示例(可以在安排了回調之后將其重置)。
編輯
更改了計時器邏輯,以取消取消時的計時器回調,以使示例更接近您的方案。
using System;
using System.ComponentModel;
using System.Threading;
void BackgroundWorkerTimerCancellation()
{
var worker = new BackgroundWorker { WorkerSupportsCancellation = true };
// Cancellation support.
var cts = new CancellationTokenSource();
var cancellationToken = cts.Token;
// Cancel worker when the CancellationTokenSource is canceled.
cancellationToken.Register(worker.CancelAsync);
// This ManualResetEvent will allow us
// to block until DoWork has finished
// (testing purposes only).
using (var outerMRE = new ManualResetEvent(false))
{
worker.DoWork += delegate
{
try
{
// Mimics your EventWaitHandle.
using (var innerMRE = new ManualResetEvent(false))
{
// Our timer which takes a looong time.
using (var timer = new Timer(_ => innerMRE.Set(), null, 10000 /* 10 seconds */, Timeout.Infinite))
{
// Wire cancellation.
cancellationToken.Register(() =>
{
// Cancel the timer (callback will never execute).
timer.Change(Timeout.Infinite, Timeout.Infinite);
// Signal wait handle immediately when canceled.
// It's lack of this call which is making
// your BackgroundWorker run indefinitely
// when the timer is canceled.
innerMRE.Set();
});
// Block until timer callback runs or until
// the CancellationTokenSource is canceled.
innerMRE.WaitOne();
}
}
}
finally
{
// Allow the outerMRE.WaitOne() call to complete.
outerMRE.Set();
}
};
// Start the worker (non-blocking).
worker.RunWorkerAsync();
// Schedule auto-cancellation after 1 second (non-blocking).
cts.CancelAfter(TimeSpan.FromSeconds(1));
// Block until DoWork has finished.
// Will take 10 seconds without cancellation,
// or one second with cancellation.
outerMRE.WaitOne();
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.