DoWork
backgroundworker1
WakeUp
通过课程while CancellationPending
设置等待计时器,效果很好。
现在的问题是有时我的DoWork
以无限循环结束。所以在WaitOne
内部调用DoWork
,BackgroundWorker
设置等待计时器的东西并等待线程直到定时器触发。
我需要让BackgroundWorker
立即关闭,但我必须保留对CancellationPending
的引用,以便我可以跟踪程序中的每个警报。为什么backgroundworker
花了这么长时间?它似乎永远不会完成。是否有某种方法可以杀死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();
}
,而不必像这样在while循环中等待这么久?
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);
}
}
}
WakeUp.cs
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;
可能的解决方案:
{{1}}
但我试图避免这种类型的代码。
答案 0 :(得分:1)
我不熟悉您正在进行PInvoking的kernel32 API,因此我将使用System.Threading.Timer
给出一个基本示例(可以在调度回调后重置)。< / p>
修改强>
更改了计时器逻辑,以便在取消时禁止计时器回调,以使示例更接近您的方案。
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();
}
}