取消BackgroundWorker

时间:2014-06-30 01:27:25

标签: c# .net-4.5 backgroundworker

DoWork backgroundworker1 WakeUp通过课程while CancellationPending设置等待计时器,效果很好。

现在的问题是有时我的DoWork以无限循环结束。所以在WaitOne内部调用DoWorkBackgroundWorker设置等待计时器的东西并等待线程直到定时器触发。

我需要让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}}

但我试图避免这种类型的代码。

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();
    }
}