1

所以我有一个使用 filesystemWatcher 并很好地触发事件的应用程序。FSW 将触发一堆非常接近的时间。我想创建一个在上次触发 FSW 后一小时触发的函数。

我首先尝试使用后台工作人员:(为清楚起见,所有代码都被缩短了)

namespace Devo
{
    public partial class Form1 : Form
    {
        BackgroundWorker bw = new BackgroundWorker();

        private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
        {

            if (bw.IsBusy)
            {
                bw.CancelAsync(); //this is to, in a way, reset the timer for the delayed method.
            }

            //do a lot of stuff

            bw.RunWorkerAsync();
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();

            while(sw.ElapsedMilliseconds < 180000)
            {
                if (bw.CancellationPending == true)
                {
                    sw.Stop();
                    sw.Reset();
                    e.Cancel = true;
                    return;
                }
            }

            sw.Stop();
            sw.Reset();

            DelayedMethod();
        }
    }
}

这不起作用,因为第二次bw.RunWorkerAsync()调用它显然很忙,即使调用bw.CancelAsync().

我的下一次尝试涉及一个常规线程,因为我在 SO(现在找不到链接)的某个地方读到了一个无法像我尝试做的那样“重新启动”backgroundWorker 的内容。

线程尝试几乎相同,但我想我会尝试,因为 backgroundWorker 中可能存在一些常规线程中不存在的约束。我想。

namespace Devo
{
    public partial class Form1 : Form
    {
        Thread PWC_counter_thread = new Thread(PWC_Counter);

        private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
        {

            if (PWC_counter_thread.IsAlive)
                PWC_counter_thread.Abort();

            //do a lot of stuff

            PWC_counter_thread.Start();
        }

        static void PWC_Counter()
        {
            Thread.Sleep(180000);

            DelayedMethod();
        }
    }
}

但这给了我同样的错误。在第二个电话PWC_counter_thread.Start()是忙。

我假设在第二个线程等待时存在竞争条件,在本例中为 3 分钟,并且初始 FSW 方法需要整整一秒才能执行,因此我相信对 .Abort() 的调用和.CancelAsync() 都在各自的方法完成之前完成。

现在的问题:

是否可以以我尝试的方式重新启动线程?如果是这样,我做错了什么?

我应该以另一种方式延迟我的方法调用吗?如果是这样,提示?

编辑/更新/解决方案

我从来没有像我想要的那样启动和停止线程工作,所以我找到了另一种解决我的情况的方法。

情况是我有第二个线程作为一种计时器,它会在设定的时间后调用一个方法。我的第一个线程做了一些工作,完成后它将启动第二个线程。如果第一个线程在计时器线程完成之前再次启动,它应该杀死线程并重新启动它。对我来说,这证明很难达到我想要的方式。所以我改为采用另一种方法来实现我想要的最终结果。我没有重新启动线程,而是简单地重新启动了我的第二个线程用作计数器的秒表。这给了我想要的结果。这可能是不好的做法,但它有效。

4

2 回答 2

1

在您的BackgroundWorker示例中,您可能对赛车有疑问。CancelAsync(),顾名思义,是一个异步调用,这意味着它BackgroundWorker不会立即停止工作,并且在尝试重新启动它时可能仍然工作。为避免这种情况,您应该订阅RunWorkerCompleted事件并等待它触发,然后bw.RunWorkerAsync();再再次调用。例如:

    public Form1()
    {
        bw = new BackgroundWorker();
        bw.RunWorkerCompleted += OnCompleted;
    }

    private BackgroundWorker bw;
    private ManualResetEvent completed = new ManualResetEvent(false);

    private void OnCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        completed.Set();
    }

    private void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
    {

        if (bw.IsBusy)
        {
            bw.CancelAsync();
            completed.WaitOne();
        }

        //do a lot of stuff

        completed.Reset();
        bw.RunWorkerAsync();
    }

您对Thread基于 - 的示例有多个问题。

  1. 你永远不应该打电话Thread.Abort()。相反,您应该实现一个取消机制,类似于 BackgroundWorker。创建一个bool字段(_isCancelled或其他内容)并在线程委托中定期检查它。
  2. 你不能重用一个Thread对象。你应该总是创建一个新的。
于 2014-02-19T09:09:14.447 回答
0

您最好将其封装在一个类中,并使用 System.Threading.Timer 来检测不活动。

这是我放在一起的一个例子。这个想法是您创建一个InactivityDetector具有适当的不活动阈值(在您的情况下为一个小时)和一个回调方法,当超过该不活动时间段时将调用该方法。

您必须在InactivityDetector.RegisterActivity()检测到活动时调用(例如,在您的情况下检测到文件创建)。

一旦发出了不活动回调,它将不会被再次调用,直到再次RegisterActivity()被调用(这可以防止在同一时间段的长时间不活动中多次回调)。

您的代码将通过DelayedMethod不活动操作委托。

请注意,回调在单独的线程上!

(另请注意,我没有进行任何参数验证,以使代码更短。)

using System;
using System.Threading;

namespace ConsoleApp1
{
    sealed class Program
    {
        void test()
        {
            using (var inactivityDetector = new InactivityDetector(TimeSpan.FromSeconds(2), inactivityDetected))
            {
                for (int loop = 0; loop < 3; ++loop)
                {
                    Console.WriteLine("Keeping busy once a second for 5 seconds.");

                    for (int i = 0; i < 5; ++i)
                    {
                        Thread.Sleep(1000);
                        Console.WriteLine("Registering activity");
                        inactivityDetector.RegisterActivity();
                    }

                    Console.WriteLine("Entering 3 second inactivity");
                    Thread.Sleep(3000);
                    inactivityDetector.RegisterActivity();
                }
            }
        }

        static void inactivityDetected()
        {
            Console.WriteLine("Inactivity detected.");
        }

        static void Main(string[] args)
        {
            new Program().test();
        }
    }

    public sealed class InactivityDetector: IDisposable
    {
        public InactivityDetector(TimeSpan inactivityThreshold, Action onInactivity)
        {
            _inactivityThreshold = inactivityThreshold;
            _onInactivity        = onInactivity;
            _timer               = new Timer(timerCallback, null, (int)inactivityThreshold.TotalMilliseconds, -1);
        }

        public void RegisterActivity()
        {
            _timer.Change(-1, -1);
            _timer.Change((int)_inactivityThreshold.TotalMilliseconds, -1);
        }

        private void timerCallback(object state)
        {
            _timer.Change(-1, -1);
            _onInactivity();
        }

        public void Dispose()
        {
            _timer.Dispose();
        }

        private readonly TimeSpan _inactivityThreshold;
        private readonly Action _onInactivity;
        private readonly Timer _timer;
    }
}
于 2014-02-19T09:39:42.180 回答