簡體   English   中英

取消BackgroundWorker

[英]Cancelling a BackgroundWorker

我的DoWork for backgroundworker1通過WakeUp類設置了一個等待計時器,它運行良好。

現在的問題是,有時我的while CancellationPending會以無限循環結束。 因此,在DoWork內部有一個對WaitOneDoWork設置了等待計時器的內容並等待線程直到計時器觸發。

我需要立即關閉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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM