C#每隔X分钟运行一个线程,但前提是该线程尚未运行

Kei*_*Jr. 62 c# timer

我有一个C#程序需要每隔X分钟调度一个线程,但前提是先前调度的线程(从X分钟开始)以前还没有运行.

Timer单独一个普通的老人将无法工作(因为无论是否先前已发送的流程已完成,它每X分钟发送一次事件).

将要发送的进程在执行任务所花费的时间内变化很大 - 有时可能需要一秒钟,有时可能需要几个小时.如果它从上次启动时仍在处理,我不想再次启动该过程.

谁能提供一些有用的C#示例代码?

Grx*_*x70 55

在我看来,在这种情况下的方法是使用System.ComponentModel.BackgroundWorker类,然后IsBusy每次要分派(或不)新线程时,只需检查其属性.代码非常简单; 这是一个例子:

class MyClass
{    
    private BackgroundWorker worker;

    public MyClass()
    {
        worker = new BackgroundWorker();
        worker.DoWork += worker_DoWork;
        Timer timer = new Timer(1000);
        timer.Elapsed += timer_Elapsed;
        timer.Start();
    }

    void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        if(!worker.IsBusy)
            worker.RunWorkerAsync();
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        //whatever You want the background thread to do...
    }
}
Run Code Online (Sandbox Code Playgroud)

在我使用的这个例子中System.Timers.Timer,但我相信它也应该与其他计时器一起使用.该BackgroundWorker班还支持进度报告和取消,并采用与调度线程通信的事件驱动模型,所以你不必担心volatile变量和...

编辑

这里有更详细的例子,包括取消和进度报告:

class MyClass
{    
    private BackgroundWorker worker;

    public MyClass()
    {
        worker = new BackgroundWorker()
        {
            WorkerSupportsCancellation = true,
            WorkerReportsProgress = true
        };
        worker.DoWork += worker_DoWork;
        worker.ProgressChanged += worker_ProgressChanged;
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;

        Timer timer = new Timer(1000);
        timer.Elapsed += timer_Elapsed;
        timer.Start();
    }

    void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        if(!worker.IsBusy)
            worker.RunWorkerAsync();
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker w = (BackgroundWorker)sender;

        while(/*condition*/)
        {
            //check if cancellation was requested
            if(w.CancellationPending)
            {
                //take any necessary action upon cancelling (rollback, etc.)

                //notify the RunWorkerCompleted event handler
                //that the operation was cancelled
                e.Cancel = true; 
                return;
            }

            //report progress; this method has an overload which can also take
            //custom object (usually representing state) as an argument
            w.ReportProgress(/*percentage*/);

            //do whatever You want the background thread to do...
        }
    }

    void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        //display the progress using e.ProgressPercentage and/or e.UserState
    }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if(e.Cancelled)
        {
            //do something
        }
        else
        {
            //do something else
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,为了取消进一步执行,只需调用即可worker.CancelAsync().请注意,这是完全由用户处理的取消机制(它不支持线程中止或任何类似的开箱即用).

  • @Giorgi我认为你的方法会起作用,但我提供了一个如何实现它的例子,充分利用了`BackgroundWorker`. (2认同)

Mat*_*int 22

你可以维持一个挥发性的bool来实现你的要求:

private volatile bool _executing;

private void TimerElapsed(object state)
{
    if (_executing)
        return;

    _executing = true;

    try
    {
        // do the real work here
    }
    catch (Exception e)
    {
        // handle your error
    }
    finally
    {
        _executing = false;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个相当安全的`volatile`用来防止并发.使用`lock`或`Interlock`是更好的做法,但是长间隔(分钟)使它足够安全*. (3认同)
  • 这是基于OP问题的措辞的答案. (2认同)

sco*_*ttm 9

您可以在已过去的回调中禁用和启用计时器.

public void TimerElapsed(object sender, EventArgs e)
{
  _timer.Stop();

  //Do Work

  _timer.Start();
}
Run Code Online (Sandbox Code Playgroud)

  • 重要的是要注意,计时器现在将每隔`(X分钟)+(无论此方法执行多长时间)`,而不是每隔X分钟触发一次,并跳过事件仍在执行的时间. (6认同)

sa_*_*213 6

你可以只使用System.Threading.Timer,只是设置TimeoutInfinite你处理你的数据/方法之前,那么当它完成重启Timer准备下一次呼叫.

    private System.Threading.Timer _timerThread;
    private int _period = 2000;

    public MainWindow()
    {
        InitializeComponent();

        _timerThread = new System.Threading.Timer((o) =>
         {
             // Stop the timer;
             _timerThread.Change(-1, -1);

             // Process your data
             ProcessData();

             // start timer again (BeginTime, Interval)
             _timerThread.Change(_period, _period);
         }, null, 0, _period);
    }

    private void ProcessData()
    {
        // do stuff;
    }
Run Code Online (Sandbox Code Playgroud)