首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >取消BackgroundWorker

取消BackgroundWorker
EN

Stack Overflow用户
提问于 2014-06-30 01:27:25
回答 1查看 3K关注 0票数 0

我的DoWork for backgroundworker1通过类WakeUp设置等待定时器,它运行良好。

现在的问题是,有时我的while CancellationPending以无限循环结束。因此,在DoWork内部有一个对WaitOne的调用,DoWork设置等待定时器,并等待线程直到计时器触发。

我需要BackgroundWorker立即关闭,但我也必须保持对BackgroundWorker的引用,以便我可以跟踪我的程序中的每一个警报。为什么CancellationPending花了这么长时间?它似乎永远也不会结束。有什么方法不需要像这样等待这么长的时间循环就可以杀死backgroundworker吗?

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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);
        }
    }
}

可能的解决办法:

代码语言:javascript
运行
复制
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;

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-06-30 02:19:24

我不熟悉您是kernel32的PInvoking API,所以我将给您一个使用System.Threading.Timer的基本示例(可以在计划回调之后重新设置)。

编辑

更改计时器逻辑以抑制取消时的计时器回调,以便使示例更接近您的场景。

代码语言:javascript
运行
复制
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();
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24481879

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档