Mon DoWork
pour backgroundworker1
définit un délai d'attente via une classe WakeUp
, cela fonctionne bien.Annulation d'un BackgroundWorker
Le problème actuel est que parfois mon while CancellationPending
se termine par une boucle infinie. Donc, à l'intérieur de DoWork
il y a un appel à WaitOne
, le DoWork
définit la minuterie d'attente et attend le thread jusqu'à ce que le minuteur se déclenche.
J'ai besoin pour le BackgroundWorker
de fermer tout de suite, mais aussi je dois garder une référence à BackgroundWorker
afin que je puisse garder une trace de chaque alarme dans mon programme. Pourquoi CancellationPending
prend tellement de temps? Ça ne semble jamais finir. Y at-il un moyen de tuer le backgroundworker
sans avoir à attendre comme ça dans une boucle pendant si longtemps?
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);
}
}
}
Solution possible:
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;
mais je tentais d'éviter ce type de code.
Afficher le code où BackgroundWorker est annulé. Si vous voulez que BackgroundWorker annule rapidement, vérifiez rapidement. – Paparazzi
Comment suis-je censé vérifier quand DoWork bloque? – Giuseppe
Vous appelez la minuterie que les appels annule le travailleur d'arrière-plan dans le travailleur d'arrière-plan d'une manière bloquante, puis appelle un autre dowork? – Paparazzi