IT TIP

C #은 X 분마다 스레드를 실행하지만 해당 스레드가 아직 실행되고 있지 않은 경우에만

itqueen 2020. 11. 30. 20:30
반응형

C #은 X 분마다 스레드를 실행하지만 해당 스레드가 아직 실행되고 있지 않은 경우에만


X 분마다 스레드를 디스패치해야하는 C # 프로그램이 있지만 이전에 디스패치 된 스레드 (X 분에서)가 현재 실행 중이 아닌 경우에만 해당됩니다 .

Timer이전에 전달 된 프로세스가 아직 완료되었는지 여부에 관계없이 X 분마다 이벤트를 전달하기 때문에 일반 오래된 것만은 작동하지 않습니다.

파견 될 프로세스는 작업을 수행하는 데 걸리는 시간이 크게 다릅니다. 때로는 1 초가 걸리고 때로는 몇 시간이 걸릴 수도 있습니다. 마지막으로 시작된 이후로 처리중인 경우 프로세스를 다시 시작하고 싶지 않습니다.

누구나 작동하는 C # 샘플 코드를 제공 할 수 있습니까?


제 생각에이 상황에서가는 방법은 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...
    }
}

이 예에서는를 사용 System.Timers.Timer했지만 다른 타이머에서도 작동해야한다고 생각합니다. BackgroundWorker클래스는 진행률보고 및 취소도 지원하고 디스패치 스레드와의 이벤트 기반 통신 모델을 사용하므로 휘발성 변수 등에 대해 걱정할 필요가 없습니다.

편집하다

다음은 취소 및 진행보고를 포함하여보다 정교한 예입니다.

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
        }
    }
}

그런 다음 추가 실행을 취소하려면 worker.CancelAsync(). 이것은 완전히 사용자가 처리하는 취소 메커니즘입니다 (스레드 중단 또는 기본적으로 이와 유사한 것을 지원하지 않음).


요청한 것을 달성하기 위해 휘발성 부울을 유지할 수 있습니다.

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;
    }
}

경과 된 콜백에서 타이머를 비활성화 및 활성화 할 수 있습니다.

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

  //Do Work

  _timer.Start();
}

당신은 단지를 사용할 수 있습니다 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;
    }

여기 내 게시물 에서 PeriodicTaskFactory 사용

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

Task task = PeriodicTaskFactory.Start(() =>
{
    Console.WriteLine(DateTime.Now);
    Thread.Sleep(5000);
}, intervalInMilliseconds: 1000, synchronous: true, cancelToken: cancellationTokenSource.Token);

Console.WriteLine("Press any key to stop iterations...");
Console.ReadKey(true);

cancellationTokenSource.Cancel();

Console.WriteLine("Waiting for the task to complete...");

Task.WaitAny(task);

아래 출력은 간격이 1000 밀리 초로 설정되어 있어도 작업 작업의 작업이 완료 될 때까지 각 반복이 시작되지 않음을 보여줍니다. 이는 synchronous: true선택적 매개 변수를 사용하여 수행 됩니다.

Press any key to stop iterations...
9/6/2013 1:01:52 PM
9/6/2013 1:01:58 PM
9/6/2013 1:02:04 PM
9/6/2013 1:02:10 PM
9/6/2013 1:02:16 PM
Waiting for the task to complete...
Press any key to continue . . .

최신 정보

PeriodicTaskFactory로 "건너 뛰는 이벤트"동작을 원한다면 단순히 동기 옵션을 사용하지 않고 Bob이 여기에서 한 것처럼 Monitor.TryEnter를 구현하십시오. https://stackoverflow.com/a/18665948/222434

Task task = PeriodicTaskFactory.Start(() =>
{
    if (!Monitor.TryEnter(_locker)) { return; }  // Don't let  multiple threads in here at the same time.

    try
    {
        Console.WriteLine(DateTime.Now);
        Thread.Sleep(5000);
    }
    finally
    {
        Monitor.Exit(_locker);
    }

}, intervalInMilliseconds: 1000, synchronous: false, cancelToken: cancellationTokenSource.Token);

에 대한 좋은 점은 PeriodicTaskFactory모든 TPL API와 함께 사용할 수있는 태스크가 반환된다는 것입니다 (예 : Task.Wait연속 등).


작업 전에 타이머를 중지하고 작업 완료 후 다시 시작할 수 있습니다. 이렇게하면 일정 시간 간격으로 주기적으로 테이크를 수행 할 수 있습니다.

public void myTimer_Elapsed(object sender, EventArgs e)
{
    myTimer.Stop();
    // Do something you want here.
    myTimer.Start();
}

타이머의 콜백이 백그라운드 스레드에서 실행되도록하려면 System.Threading.Timer를 사용할 수 있습니다 . 이 Timer 클래스를 사용하면 " Timeout.Infinite정기 신호를 비활성화하도록 지정"할 수 있습니다 . 생성자의 일부로 , 타이머가 한 번만 실행되도록합니다.

그런 다음 첫 번째 타이머의 콜백이 실행되고 완료 될 때 새 타이머를 구성하여 여러 타이머가 발생할 준비가 될 때까지 예약되지 않도록 할 수 있습니다.

여기서 장점은 한 번에 "다음 이벤트"이상을 예약하지 않으므로 타이머를 만든 다음 반복적으로 취소하지 않는다는 것입니다.


타이머와 세마포어 사용, 휘발성 변수, TPL 사용, Quartz 등과 같은 오픈 소스 스케줄링 도구 사용에 이르기까지이를 수행하는 데는 적어도 20 가지 다른 방법이 있습니다.

스레드를 생성하는 것은 비용이 많이 드는 작업이므로 ONE을 생성하고 백그라운드에서 실행되도록 두지 마십시오. 대부분의 시간을 IDLE로 소비하므로 시스템에 실제로 낭비가 발생하지 않습니다. 주기적으로 일어나서 일을 한 다음 그 시간 동안 다시 잠자리에 든다. 작업이 얼마나 오래 걸리든 관계없이 새 작업을 시작하기 전에 완료 한 후 항상 최소한 "waitForWork"시간 범위를 기다립니다.

    //wait 5 seconds for testing purposes
    static TimeSpan waitForWork = new TimeSpan(0, 0, 0, 5, 0);
    static ManualResetEventSlim shutdownEvent = new ManualResetEventSlim(false);
    static void Main(string[] args)
    {
        System.Threading.Thread thread = new Thread(DoWork);
        thread.Name = "My Worker Thread, Dude";
        thread.Start();

        Console.ReadLine();
        shutdownEvent.Set();
        thread.Join();
    }

    public static void DoWork()
    {
        do
        {
            //wait for work timeout or shudown event notification
            shutdownEvent.Wait(waitForWork);

            //if shutting down, exit the thread
            if(shutdownEvent.IsSet)
                return;

            //TODO: Do Work here


        } while (true);

    }

System.Threading.Timer를 사용할 수 있습니다. 트릭은 초기 시간 만 설정하는 것입니다. 이전 간격이 완료되거나 작업이 완료되면 초기 시간이 다시 설정됩니다 (작업이 간격보다 오래 걸리는 경우 발생 함). 다음은 샘플 코드입니다.

class Program
{


    static System.Threading.Timer timer;
    static bool workAvailable = false;
    static int timeInMs = 5000;
    static object o = new object(); 

    static void Main(string[] args)
    {
        timer = new Timer((o) =>
            {
                try
                {
                    if (workAvailable)
                    {
                        // do the work,   whatever is required.
                        // if another thread is started use Thread.Join to wait for the thread to finish
                    }
                }
                catch (Exception)
                {
                    // handle
                }
                finally
                {
                    // only set the initial time, do not set the recurring time
                    timer.Change(timeInMs, Timeout.Infinite);
                }
            });

        // only set the initial time, do not set the recurring time
        timer.Change(timeInMs, Timeout.Infinite);
    }

왜 타이머를 사용하지 Monitor.TryEnter()않습니까? OnTimerElapsed()이전 스레드가 완료되기 전에를 다시 호출 하면 무시되고 타이머가 다시 실행될 때까지 다른 시도가 다시 발생하지 않습니다.

private static readonly object _locker = new object();

    private void OnTimerElapsed(object sender, ElapsedEventArgs e)
    {
        if (!Monitor.TryEnter(_locker)) { return; }  // Don't let  multiple threads in here at the same time.

        try
        {
            // do stuff
        }
        finally
        {
            Monitor.Exit(_locker);
        }
    }

나는 얼마 전에 같은 문제가 있었고 내가 한 일은 lock {} 문을 사용하는 것뿐이었습니다 . 이것으로 Timer가 어떤 일을하려고하더라도 lock-Block이 끝날 때까지 기다려야합니다.

lock
{    
     // this code will never be interrupted or started again until it has finished
} 

이것은 확실히 할 수있는 좋은 방법입니다. 여러분의 프로세스는 중단없이 끝까지 작동 할 것입니다.


이 질문 에는 TPL의 일부 기능을 기반으로 한 약간 새로운 답변을 포함 하여 이미 많은 좋은 답변 이 있습니다. 하지만 여기서 부족함을 느낍니다.

  1. TPL 기반 솔루션 a) 실제로 여기에 완전히 포함되어 있지는 않지만 다른 답변을 참조합니다 .b) 단일 메서드에서 타이밍 메커니즘을 사용 async/ await구현하는 방법을 보여주지 않습니다 .c) 참조 된 구현은 다음과 같습니다. 특정 질문에 대한 근본적인 관련 포인트를 다소 모호하게 만드는 상당히 복잡 합니다.
  2. 여기의 원래 질문은 원하는 구현의 특정 매개 변수에 대해 다소 모호합니다 (일부는 주석에서 명확하게 설명되어 있음). 동시에, 다른 독자들은 비슷하지만 동일하지는 않을 수 있으며, 원하는 다양한 디자인 옵션을 다루는 사람은 아무도 없습니다.
  3. 저는 특히 코드를 단순화하는 방식으로 인해 Taskasync/ await이 방식 으로주기적인 동작을 구현하는 것을 좋아 합니다. 특히 async/ await기능은 연속 / 콜백 구현 세부 사항에 의해 분리 될 수있는 코드를 가져와 단일 메서드에서 자연스럽고 선형적인 논리를 보존하는 데 매우 유용합니다. 그러나 여기에 그 단순함을 보여주는 대답은 없습니다.

그래서, 그 근거로이 질문에 또 다른 답을 추가하도록 동기를 부여했습니다.


나에게 가장 먼저 고려해야 할 것은 " 여기서 원하는 정확한 행동은 무엇 인가?"입니다. 여기서 질문은 기본 전제에서 시작됩니다. 타이머에 의해 시작된 기간 작업은 작업이 타이머 기간보다 오래 걸리더라도 동시에 실행되지 않아야합니다. 그러나 다음을 포함하여 전제를 이행 할 수있는 여러 가지 방법이 있습니다.

  1. 작업이 실행되는 동안에는 타이머를 실행하지 마십시오.
  2. 타이머를 실행합니다 (여기에 제시된 옵션과 나머지 옵션은 모두 작업 실행 중에 타이머가 계속 실행된다고 가정합니다).하지만 작업이 타이머 기간보다 오래 걸리면 작업이 완료된 후 즉시 작업을 다시 실행합니다. 이전 타이머 틱.
  3. 타이머 틱에서만 작업 실행을 시작하십시오. 작업이 타이머 기간보다 오래 걸리면 현재 작업이 실행되는 동안 새 작업을 시작하지 말고 현재 작업이 완료된 후에도 다음 타이머 틱까지 새 작업을 시작하지 마십시오.
  4. 작업이 타이머 간격보다 오래 걸리는 경우 작업이 완료된 후 즉시 다시 실행할뿐만 아니라 작업이 "잡힐"때까지 필요한만큼 실행합니다. 즉, 시간이 지남에 따라 모든 타이머 틱에 대해 한 번씩 작업을 실행하도록 최선을 다하십시오 .

의견을 바탕으로 # 3 옵션이 OP의 원래 요청과 가장 가깝게 일치한다고 생각하지만 # 1 옵션도 작동 할 것 같습니다. 그러나 옵션 # 2 및 # 4는 다른 사람보다 선호 될 수 있습니다.

다음 코드 예제에서는 다섯 가지 방법으로 이러한 옵션을 구현했습니다 (둘 중 두 가지는 옵션 # 3을 구현하지만 약간 다른 방식으로 구현). 물론, 자신의 필요에 맞는 적절한 구현을 선택할 것입니다. 하나의 프로그램에 다섯 가지가 모두 필요하지는 않을 것입니다! :)

요점은 이러한 모든 구현에서 자연스럽고 매우 간단한 방법으로 작업을 기간이면서 동시에 실행되지 않는 방식으로 실행한다는 것입니다. 즉, 타이머 기반 실행 모델을 효과적으로 구현하는 동시에 질문의 기본 요청에 따라 한 번에 하나의 스레드에서만 작업이 실행되도록합니다.

이 예제 에서는 예외 기반 모델을 깔끔하고 간단한 방식으로 처리하는 CancellationTokenSource이점을 활용하여 기간 작업을 중단하는 방법을 보여줍니다 await.

class Program
{
    const int timerSeconds = 5, actionMinSeconds = 1, actionMaxSeconds = 7;

    static Random _rnd = new Random();

    static void Main(string[] args)
    {
        Console.WriteLine("Press any key to interrupt timer and exit...");
        Console.WriteLine();

        CancellationTokenSource cancelSource = new CancellationTokenSource();

        new Thread(() => CancelOnInput(cancelSource)).Start();

        Console.WriteLine(
            "Starting at {0:HH:mm:ss.f}, timer interval is {1} seconds",
            DateTime.Now, timerSeconds);
        Console.WriteLine();
        Console.WriteLine();

        // NOTE: the call to Wait() is for the purpose of this
        // specific demonstration in a console program. One does
        // not normally use a blocking wait like this for asynchronous
        // operations.

        // Specify the specific implementation to test by providing the method
        // name as the second argument.
        RunTimer(cancelSource.Token, M1).Wait();
    }

    static async Task RunTimer(
        CancellationToken cancelToken, Func<Action, TimeSpan, Task> timerMethod)
    {
        Console.WriteLine("Testing method {0}()", timerMethod.Method.Name);
        Console.WriteLine();

        try
        {
            await timerMethod(() =>
            {
                cancelToken.ThrowIfCancellationRequested();
                DummyAction();
            }, TimeSpan.FromSeconds(timerSeconds));
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine();
            Console.WriteLine("Operation cancelled");
        }
    }

    static void CancelOnInput(CancellationTokenSource cancelSource)
    {
        Console.ReadKey();
        cancelSource.Cancel();
    }

    static void DummyAction()
    {
        int duration = _rnd.Next(actionMinSeconds, actionMaxSeconds + 1);

        Console.WriteLine("dummy action: {0} seconds", duration);
        Console.Write("    start: {0:HH:mm:ss.f}", DateTime.Now);
        Thread.Sleep(TimeSpan.FromSeconds(duration));
        Console.WriteLine(" - end: {0:HH:mm:ss.f}", DateTime.Now);
    }

    static async Task M1(Action taskAction, TimeSpan timer)
    {
        // Most basic: always wait specified duration between
        // each execution of taskAction
        while (true)
        {
            await Task.Delay(timer);
            await Task.Run(() => taskAction());
        }
    }

    static async Task M2(Action taskAction, TimeSpan timer)
    {
        // Simple: wait for specified interval, minus the duration of
        // the execution of taskAction. Run taskAction immediately if
        // the previous execution too longer than timer.

        TimeSpan remainingDelay = timer;

        while (true)
        {
            if (remainingDelay > TimeSpan.Zero)
            {
                await Task.Delay(remainingDelay);
            }

            Stopwatch sw = Stopwatch.StartNew();
            await Task.Run(() => taskAction());
            remainingDelay = timer - sw.Elapsed;
        }
    }

    static async Task M3a(Action taskAction, TimeSpan timer)
    {
        // More complicated: only start action on time intervals that
        // are multiples of the specified timer interval. If execution
        // of taskAction takes longer than the specified timer interval,
        // wait until next multiple.

        // NOTE: this implementation may drift over time relative to the
        // initial start time, as it considers only the time for the executed
        // action and there is a small amount of overhead in the loop. See
        // M3b() for an implementation that always executes on multiples of
        // the interval relative to the original start time.

        TimeSpan remainingDelay = timer;

        while (true)
        {
            await Task.Delay(remainingDelay);

            Stopwatch sw = Stopwatch.StartNew();
            await Task.Run(() => taskAction());

            long remainder = sw.Elapsed.Ticks % timer.Ticks;

            remainingDelay = TimeSpan.FromTicks(timer.Ticks - remainder);
        }
    }

    static async Task M3b(Action taskAction, TimeSpan timer)
    {
        // More complicated: only start action on time intervals that
        // are multiples of the specified timer interval. If execution
        // of taskAction takes longer than the specified timer interval,
        // wait until next multiple.

        // NOTE: this implementation computes the intervals based on the
        // original start time of the loop, and thus will not drift over
        // time (not counting any drift that exists in the computer's clock
        // itself).

        TimeSpan remainingDelay = timer;
        Stopwatch swTotal = Stopwatch.StartNew();

        while (true)
        {
            await Task.Delay(remainingDelay);
            await Task.Run(() => taskAction());

            long remainder = swTotal.Elapsed.Ticks % timer.Ticks;

            remainingDelay = TimeSpan.FromTicks(timer.Ticks - remainder);
        }
    }

    static async Task M4(Action taskAction, TimeSpan timer)
    {
        // More complicated: this implementation is very different from
        // the others, in that while each execution of the task action
        // is serialized, they are effectively queued. In all of the others,
        // if the task is executing when a timer tick would have happened,
        // the execution for that tick is simply ignored. But here, each time
        // the timer would have ticked, the task action will be executed.
        //
        // If the task action takes longer than the timer for an extended
        // period of time, it will repeatedly execute. If and when it
        // "catches up" (which it can do only if it then eventually
        // executes more quickly than the timer period for some number
        // of iterations), it reverts to the "execute on a fixed
        // interval" behavior.

        TimeSpan nextTick = timer;
        Stopwatch swTotal = Stopwatch.StartNew();

        while (true)
        {
            TimeSpan remainingDelay = nextTick - swTotal.Elapsed;

            if (remainingDelay > TimeSpan.Zero)
            {
                await Task.Delay(remainingDelay);
            }

            await Task.Run(() => taskAction());
            nextTick += timer;
        }
    }
}

마지막 메모 :이 Q & A를 다른 질문의 중복으로 따라 간 후에 발견했습니다. 여기와 달리 다른 질문에서 OP는 System.Windows.Forms.Timer클래스를 사용하고 있음을 구체적으로 언급했습니다 . 물론이 클래스는 주로 TickUI 스레드에서 이벤트가 발생 하는 멋진 기능이 있기 때문에 사용됩니다 .

이제이 질문과이 질문에는 실제로 백그라운드 스레드에서 실행되는 작업이 포함되어 있으므로 해당 타이머 클래스의 UI 스레드 선호 동작은 이러한 시나리오에서 실제로 특별히 사용되지 않습니다. 여기에있는 코드는 "백그라운드 작업 시작"패러다임과 일치하도록 구현되지만 taskAction델리게이트가 a에서 실행 Task되고 대기 하는 대신 직접 호출 되도록 쉽게 변경할 수 있습니다 . 위에서 언급 한 구조적 이점 외에도 async/ 사용에 대한 좋은 점은 클래스 await에서 바람직한 스레드 선호 동작을 보존한다는 것 System.Windows.Forms.Timer입니다.


내가 올바르게 이해했다면 실제로 다른 스레드를 디스패치하기 전에 스레드가 실행되고 있지 않은지 확인하고 싶습니다. 클래스에 이렇게 정의 된 스레드가 있다고 가정 해 보겠습니다 .

private System.Threading.Thread myThread;

넌 할 수있어:

//inside some executed method
System.Threading.Timer t = new System.Threading.Timer(timerCallBackMethod, null, 0, 5000);

그런 다음 콜백을 추가하십시오.

private void timerCallBackMethod(object state)
{
     if(myThread.ThreadState == System.Threading.ThreadState.Stopped || myThread.ThreadState == System.Threading.ThreadState.Unstarted)
     {
        //dispatch new thread
     }
}

이것은 당신이 원하는 것을해야합니다. 스레드를 실행 한 다음 완료 될 때까지 스레드를 결합합니다. 스레드가 너무 일찍 실행되고 있지 않은지 확인하기 위해 타이머 루프로 이동 한 다음 다시 꺼져 실행됩니다.

using System.Threading;

public class MyThread
{
    public void ThreadFunc()
    {
        // do nothing apart from sleep a bit
        System.Console.WriteLine("In Timer Function!");
        Thread.Sleep(new TimeSpan(0, 0, 5));
    }
};

class Program
{
    static void Main(string[] args)
    {
        bool bExit = false;
        DateTime tmeLastExecuted;

        // while we don't have a condition to exit the thread loop
        while (!bExit)
        {
            // create a new instance of our thread class and ThreadStart paramter
            MyThread myThreadClass = new MyThread();
            Thread newThread = new Thread(new ThreadStart(myThreadClass.ThreadFunc));

            // just as well join the thread until it exits
            tmeLastExecuted = DateTime.Now; // update timing flag
            newThread.Start();
            newThread.Join();

            // when we are in the timing threshold to execute a new thread, we can exit
            // this loop
            System.Console.WriteLine("Sleeping for a bit!");

            // only allowed to execute a thread every 10 seconds minimum
            while (DateTime.Now - tmeLastExecuted < new TimeSpan(0, 0, 10));
            {
                Thread.Sleep(100); // sleep to make sure program has no tight loops
            }

            System.Console.WriteLine("Ok, going in for another thread creation!");
        }
    }
}

다음과 같이 생성해야합니다.

타이머 기능! 잠깐만 요! 좋아요, 다른 스레드 생성을 시작합니다! 타이머 기능! 잠깐만 요! 좋아요, 다른 스레드 생성을 시작합니다! 타이머 기능! ... ...

도움이 되었기를 바랍니다! SR


이것의 용기는 ExecuteTaskCallback방법입니다. 이 비트는 일부 작업을 수행하는 데 부과되지만 아직 수행하지 않은 경우에만 해당됩니다. 이를 위해 처음에는 메서드 에서 신호를 받도록 설정된 ManualResetEvent( canExecute)를 사용했습니다 StartTaskCallbacks.

내가 사용하는 방법에 유의하십시오 canExecute.WaitOne(0). 0은 ( MSDN ) WaitOne상태로 즉시 반환 됨을 의미합니다 . 0이 생략되면 결국 작업을 실행하기위한 모든 호출이 발생 하며 이는 상당히 비참 할 수 있습니다.WaitHandleExecuteTaskCallback

또 다른 중요한 것은 처리를 깨끗하게 끝낼 수 있다는 것입니다. 나는 다른 작업이 진행되는 동안 그렇게하는 것이 TimerStopTaskCallbacks바람직하기 때문에 에서 더 이상의 메서드를 실행하지 못하도록 선택했습니다 . 이렇게하면 새로운 작업이 수행되지 않고에 대한 후속 호출 canExecute.WaitOne();이 마지막 작업이있는 경우 실제로 처리됩니다.

private static void ExecuteTaskCallback(object state)
{
    ManualResetEvent canExecute = (ManualResetEvent)state;

    if (canExecute.WaitOne(0))
    {
        canExecute.Reset();
        Console.WriteLine("Doing some work...");
        //Simulate doing work.
        Thread.Sleep(3000);
        Console.WriteLine("...work completed");
        canExecute.Set();
    }
    else
    {
        Console.WriteLine("Returning as method is already running");
    }
}

private static void StartTaskCallbacks()
{
    ManualResetEvent canExecute = new ManualResetEvent(true),
        stopRunning = new ManualResetEvent(false);
    int interval = 1000;

    //Periodic invocations. Begins immediately.
    Timer timer = new Timer(ExecuteTaskCallback, canExecute, 0, interval);

    //Simulate being stopped.
    Timer stopTimer = new Timer(StopTaskCallbacks, new object[]
    {
        canExecute, stopRunning, timer
    }, 10000, Timeout.Infinite);

    stopRunning.WaitOne();

    //Clean up.
    timer.Dispose();
    stopTimer.Dispose();
}

private static void StopTaskCallbacks(object state)
{
    object[] stateArray = (object[])state;
    ManualResetEvent canExecute = (ManualResetEvent)stateArray[0];
    ManualResetEvent stopRunning = (ManualResetEvent)stateArray[1];
    Timer timer = (Timer)stateArray[2];

    //Stop the periodic invocations.
    timer.Change(Timeout.Infinite, Timeout.Infinite);

    Console.WriteLine("Waiting for existing work to complete");
    canExecute.WaitOne();
    stopRunning.Set();
}

더 가벼운 개체이므로 스레드 대신 타이머를 사용하는 것이 좋습니다. 목표를 달성하기 위해 다음을 수행 할 수 있습니다.

using System.Timers;

namespace sample_code_1
{
    public class ClassName
    {
        Timer myTimer;
        static volatile bool isRunning;

        public OnboardingTaskService()
        {
             myTimer= new Timer();
             myTimer.Interval = 60000;
             myTimer.Elapsed += myTimer_Elapsed;
             myTimer.Start();
        }

        private void myTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (isRunning) return;
            isRunning = true;
            try
            {
                //Your Code....
            }
            catch (Exception ex)
            {
                //Handle Exception
            }
            finally { isRunning = false; }
        }
    }
}

도움이되는지 알려주세요.

참고 URL : https://stackoverflow.com/questions/12570324/c-sharp-run-a-thread-every-x-minutes-but-only-if-that-thread-is-not-running-alr

반응형