매 x 분마다 깨어나서 작동하는 x 개의 스레드를 만드는 Windows 서비스를 만들고 싶습니다.C# 4.0에서 상수 실행 스레드가있는 Windows 서비스에 가장 적합한 솔루션
필자는 작업 스케줄링 또는 병렬 프레임 워크가 상수가 아닌 시작, 완료 및 완료 작업에 가장 적합하므로이 유형의 작업에 적합하지 않다고 생각합니다.
이 접근법을 위해 스레드 풀을 활용할 것인가, 아니면 좋은 해결책에 대한 조언이있는 사람이 있습니까?
매 x 분마다 깨어나서 작동하는 x 개의 스레드를 만드는 Windows 서비스를 만들고 싶습니다.C# 4.0에서 상수 실행 스레드가있는 Windows 서비스에 가장 적합한 솔루션
필자는 작업 스케줄링 또는 병렬 프레임 워크가 상수가 아닌 시작, 완료 및 완료 작업에 가장 적합하므로이 유형의 작업에 적합하지 않다고 생각합니다.
이 접근법을 위해 스레드 풀을 활용할 것인가, 아니면 좋은 해결책에 대한 조언이있는 사람이 있습니까?
정말로 하나의 스레드 만 필요한 것 같습니다.
다음은 정확히 이런 종류의 작업을 위해 만든 도우미 클래스입니다.
이tasks.Shutdown();
(또는, backgroundThread와 Start
전화 :
var tasks = new MyPeriodicTasks();
tasks.Start();
그리고 서비스를 종료 중 : 작업을 시작하려면 다음
class MyPeriodicTasks : PeriodicMultiple
{
// The first task will start 30 seconds after this class is instantiated and started:
protected override TimeSpan FirstInterval { get { return TimeSpan.FromSeconds(30); } }
public MyPeriodicTasks()
{
Tasks = new[] {
new Task { Action = task1, MinInterval = TimeSpan.FromMinutes(5) },
new Task { Action = task2, MinInterval = TimeSpan.FromMinutes(15) },
};
}
private void task1() { /* code that gets executed once every 5 minutes */ }
private void task2() { /* code that gets executed once every 15 minutes */ }
}
: 여기 당신이 그것을 사용하는 방법은 사실, 다음 당신은 Shutdown을 호출 할 필요가 없지만, 작업은 무언가를하는 도중 종료 될 수 있습니다.)
여기에 실제 코드입니다 :
/// <summary>
/// Encapsulates a class performing a certain activity periodically, which can be initiated once
/// and then permanently shut down, but not paused/resumed. The class owns its own separate
/// thread, and manages this thread all by itself. The periodic task is executed on this thread.
/// <para>The chief differences to <see cref="System.Threading.Timer"/> are as follows. This
/// class will never issue overlapping activities, even if an activity takes much longer than the interval;
/// the interval is between the end of the previous occurrence of the activity and the start of the next.
/// The activity is executed on a foreground thread (by default), and thus will complete once started,
/// unless a catastrophic abort occurs. When shutting down the activity, it's possible to wait until the
/// last occurrence, if any, has completed fully.</para>
/// </summary>
public abstract class Periodic
{
private Thread _thread;
private CancellationTokenSource _cancellation;
private ManualResetEvent _exited;
/// <summary>
/// Override to indicate how long to wait between the call to <see cref="Start"/> and the first occurrence
/// of the periodic activity.
/// </summary>
protected abstract TimeSpan FirstInterval { get; }
/// <summary>
/// Override to indicate how long to wait between second and subsequent occurrences of the periodic activity.
/// </summary>
protected abstract TimeSpan SubsequentInterval { get; }
/// <summary>
/// Override with a method that performs the desired periodic activity. If this method throws an exception
/// the thread will terminate, but the <see cref="LastActivity"/> will occur nevertheless.
/// </summary>
protected abstract void PeriodicActivity();
/// <summary>
/// Override with a method that performs an activity on the same thread as <see cref="PeriodicActivity"/> during
/// shutdown, just before signalling that the shutdown is complete. The default implementation of this method
/// does nothing. This method is guaranteed to be called during a shutdown, even if the shutdown is due to an
/// exception propagating outside of <see cref="PeriodicActivity"/>.
/// </summary>
protected virtual void LastActivity() { }
/// <summary>
/// Returns false before the first call to <see cref="Start"/> and after the first call to <see cref="Shutdown"/>;
/// true between them.
/// </summary>
public bool IsRunning { get { return _cancellation != null && !_cancellation.IsCancellationRequested; } }
/// <summary>
/// Schedules the periodic activity to start occurring. This method may only be called once.
/// </summary>
/// <param name="backgroundThread">By default (false) the class will use a foreground thread, preventing application shutdown until the thread has terminated. If true, a background thread will be created instead.</param>
public virtual void Start(bool backgroundThread = false)
{
if (_thread != null)
throw new InvalidOperationException(string.Format("\"Start\" called multiple times ({0})", GetType().Name));
_exited = new ManualResetEvent(false);
_cancellation = new CancellationTokenSource();
_thread = new Thread(threadProc) { IsBackground = backgroundThread };
_thread.Start();
}
private volatile bool _periodicActivityRunning = false;
/// <summary>
/// Causes the periodic activity to stop occurring. If called while the activity is being performed,
/// will wait until the activity has completed before returning. Ensures that <see cref="IsRunning"/>
/// is false once this method returns.
/// </summary>
public virtual bool Shutdown(bool waitForExit)
{
if (waitForExit && _periodicActivityRunning && Thread.CurrentThread.ManagedThreadId == _thread.ManagedThreadId)
throw new InvalidOperationException("Cannot call Shutdown(true) from within PeriodicActivity() on the same thread (this would cause a deadlock).");
if (_cancellation == null || _cancellation.IsCancellationRequested)
return false;
_cancellation.Cancel();
if (waitForExit)
_exited.WaitOne();
return true;
}
private void threadProc()
{
try
{
_cancellation.Token.WaitHandle.WaitOne(FirstInterval);
while (!_cancellation.IsCancellationRequested)
{
_periodicActivityRunning = true;
PeriodicActivity();
_periodicActivityRunning = false;
_cancellation.Token.WaitHandle.WaitOne(SubsequentInterval);
}
}
finally
{
try { LastActivity(); }
finally { _exited.Set(); }
}
}
}
/// <summary>
/// <para>Encapsulates a class performing multiple related yet independent tasks on the same thread
/// at a certain minimum interval each. Schedules the activity that is the most late at every opportunity,
/// but will never execute more than one activity at a time (as they all share the same thread).</para>
/// </summary>
public abstract class PeriodicMultiple : Periodic
{
/// <summary>
/// Used to define the activities to be executed periodically.
/// </summary>
protected sealed class Task
{
/// <summary>The activity to be performed.</summary>
public Action Action;
/// <summary>The mimimum interval at which this activity should be repeated. May be delayed arbitrarily though.</summary>
public TimeSpan MinInterval;
/// <summary>Stores the last time this activity was executed.</summary>
public DateTime LastExecuted;
/// <summary>Calculates by how much this activity has been delayed. Is used internally to pick the next activity to run. Returns negative values for activities that aren't due yet.</summary>
public TimeSpan DelayedBy()
{
if (LastExecuted == default(DateTime))
return TimeSpan.FromDays(1000) - MinInterval; // to run shortest interval first when none of the tasks have ever executed
else
return (DateTime.UtcNow - LastExecuted) - MinInterval;
}
}
/// <summary>If desired, override to provide a custom interval at which the scheduler
/// should re-check whether any activity is due to start. Defaults to 1 second.</summary>
protected override TimeSpan SubsequentInterval { get { return TimeSpan.FromSeconds(1); } }
/// <summary>Initialise this with the list of activities to be executed.</summary>
protected IList<Task> Tasks;
/// <summary>For internal use.</summary>
protected sealed override void PeriodicActivity()
{
TimeSpan maxDelay = TimeSpan.MinValue;
Task maxDelayTask = null;
foreach (var task in Tasks)
{
var delayedBy = task.DelayedBy();
if (maxDelay < delayedBy && delayedBy > TimeSpan.Zero)
{
maxDelay = delayedBy;
maxDelayTask = task;
}
}
if (maxDelayTask != null)
{
maxDelayTask.LastExecuted = DateTime.UtcNow;
maxDelayTask.Action();
}
}
}
스레드가 시간 수면의 대부분을 소비하지만 작업으로 인해 있는지 확인하기 위해 1 초마다 일어나 않습니다. 이 1 초 간격은 15 분 간격으로 너무 짧을 수 있으므로 대신 30 초 (예 : SubsequentInterval
)로 줄이십시오.
희망이 있습니다. 유용합니다.
글쎄, 나는 당신의 문제가 생산자 소비자 디자인 패턴으로 해결 될 것 같다는 것을 믿습니다.
생산자는 단일 주 스레드가되고 다른 모든 스레드는 소비자 스레드가됩니다. 내 생각에 스레드 풀을 사용하는 것보다 독립적 인 스레드를 사용하는 것이 가장 좋습니다.
예 :
private Thread Worker;
public Consumer()
{
Worker = new Thread(ProcessMethod);
}
지금 processmethod에 당신은 당신이해야 할 일을. 원하는만큼의 소비자를 만듭니다.
x 스레드가 의도적으로 y 분 동안 아무 작업도하지 못하게 할 때 x 스레드를 시작하는 것은 거의 의미가 없습니다. 그냥 하나의 thread do x jobs가 있습니다. 작업을 완료하는 데 x 배의 시간이 걸리지 만 실제로는 y 분이 소요되지 않는 한 전혀 문제가되지 않습니다.
다른 이점은 서비스가 시스템의 응답성에 쉽게 영향을 줄 수없고 다른 코어가 계속 사용할 수 있다는 것입니다. 그리고 코드가 구현 및 디버그하기가 쉬워졌습니다.
System.Threading.Thread 타이머를 사용하여 작업을 활성화하십시오. 콜백은 스레드 풀 스레드에서 실행됩니다. 서비스를 시작하고 중지하는 것은 쉽습니다. 단지 타이머를 활성화/비활성화하십시오.
x 분 후에 끊임없이 실행 한 다음 깨우려면 정말 필요한 스레드가 있습니까? 난 당신을 위해 작업을 실행하는 Quartz.NET 같은 기존의 스케줄러 라이브러리를 사용하는 것이 좋습니다 생각하실 수 있습니다.
두 가지 제안이 있습니다. 먼저 서비스를 구축하려면 TopShelf을 확인하십시오. 그것은 Windows 서비스 설정의 모든 고통을 제거합니다.
둘째, Observable 클래스를 사용하여 타이머 관련 코드 또는 Quartz (구성 할 고통!)를 작성하지 않고도 타이머를 만들 수 있습니다.
public class MyService
{
private IDisposable Timer;
public void Start()
{
Timer = ObservableHelpers
.CreateMinutePulse(15) // check every 15 seconds if it's a new minute
.Subscribe(i => DoSomething());
}
public void Stop()
{
if(Timer != null)
{
Timer.Dispose();
Timer = null;
}
}
public void DoSomething()
{
// do your thing here
}
}
public static class ObservableHelpers
{
/// <summary>
/// Returns an observable that pulses every minute with the specified resolution.
/// The pulse occurs within the amount of time specified by the resolution (in seconds.)
/// Higher resolution (i.e. lower specified number of seconds) may affect execution speed.
/// </summary>
/// <returns></returns>
public static IObservable<int> CreateMinutePulse(int resolution)
{
return Observable
.Interval(TimeSpan.FromSeconds(resolution.SetWithinRange(1, 59)))
.Select(i => DateTime.Now.Minute)
.DistinctUntilChanged();
}
}
(이것은'Threading.Timer' 방법과 다른 방법을 이해하는'Periodic'의 참조)
는 여기에 몇 가지 예제 코드입니다. –
아마도 MS에서 작업 병렬 라이브러리를 사용하면됩니다. – Ryan
이 장기 실행 태스크에 태스크 라이브러리가 가장 적합하다고 생각하지 않습니다. – dagda1