Dreamine.Threading 1.0.1
이 패키지는 .NET ThreadPool이나 Task Parallel Library를 대체하려는 목적이 아닙니다.
로딩중...
검색중...
일치하는것 없음
DreamineThread.cs
이 파일의 문서화 페이지로 가기
1using Dreamine.Logging.Interfaces;
4
6
15public sealed class DreamineThread : IDreamineThread
16{
25 private readonly object _syncRoot = new();
34 private readonly List<IDreamineThreadJob> _jobs = new();
70 private readonly IDreamineLogger? _logger;
79 private readonly ManualResetEventSlim _pauseEvent = new(true);
80
89 private Thread? _thread;
98 private CancellationTokenSource? _cancellationTokenSource;
107 private long _cycleCount;
116 private DateTimeOffset? _startedAt;
125 private DateTimeOffset? _stoppedAt;
134 private string? _lastErrorMessage;
143 private bool _disposed;
144
153 public string Name { get; }
154
164
174
184
194 {
195 get => _status;
196 private set => _status = value;
197 }
198
207 public int JobCount
208 {
209 get
210 {
211 lock (_syncRoot)
212 {
213 return _jobs.Count;
214 }
215 }
216 }
217
283 DreamineThreadOptions options,
284 DreamineThreadCoreAssignment coreAssignment,
285 IThreadCyclePolicy cyclePolicy,
286 IThreadAffinityService? affinityService = null,
287 ITimerResolutionService? timerResolutionService = null,
288 IDreamineLogger? logger = null)
289 {
290 ArgumentNullException.ThrowIfNull(options);
291 ArgumentNullException.ThrowIfNull(coreAssignment);
292 ArgumentNullException.ThrowIfNull(cyclePolicy);
293
294 Options = options.Normalize();
295 Name = Options.Name;
296 CoreAssignment = coreAssignment;
297 _cyclePolicy = cyclePolicy;
298 _affinityService = affinityService;
299 _timerResolutionService = timerResolutionService;
300 _logger = logger;
301 }
302
335 public void AddJob(IDreamineThreadJob job)
336 {
337 ArgumentNullException.ThrowIfNull(job);
338
339 lock (_syncRoot)
340 {
342 _jobs.Add(job);
343 }
344 }
345
370 public void Start()
371 {
372 Thread? threadToStart = null;
373
374 lock (_syncRoot)
375 {
377
378 if (Status == DreamineThreadStatus.Running)
379 {
380 return;
381 }
382
383 _cancellationTokenSource = new CancellationTokenSource();
384 _pauseEvent.Set();
385
386 _thread = new Thread(() => Run(_cancellationTokenSource.Token))
387 {
388 IsBackground = true,
389 Name = Name,
390 Priority = MapPriority(Options.Priority)
391 };
392
394 _startedAt = DateTimeOffset.Now;
395 _stoppedAt = null;
396 _lastErrorMessage = null;
397
398 threadToStart = _thread;
399 }
400
401 // Start the thread outside the lock so that any logging or scheduling
402 // callbacks triggered by the OS during startup cannot deadlock against
403 // the lock held by callers like the manager.
404 threadToStart?.Start();
405
406 _logger?.Info($"Thread started. Name={Name}");
407 }
408
417 public void Pause()
418 {
419 bool transitioned;
420
421 lock (_syncRoot)
422 {
423 if (Status != DreamineThreadStatus.Running)
424 {
425 transitioned = false;
426 }
427 else
428 {
429 _pauseEvent.Reset();
431 transitioned = true;
432 }
433 }
434
435 if (transitioned)
436 {
437 _logger?.Info($"Thread paused. Name={Name}");
438 }
439 }
440
449 public void Resume()
450 {
451 bool transitioned;
452
453 lock (_syncRoot)
454 {
455 if (Status != DreamineThreadStatus.Paused)
456 {
457 transitioned = false;
458 }
459 else
460 {
461 _pauseEvent.Set();
463 transitioned = true;
464 }
465 }
466
467 if (transitioned)
468 {
469 _logger?.Info($"Thread resumed. Name={Name}");
470 }
471 }
472
489 [Obsolete("Stop() blocks the calling thread and risks deadlock on a SynchronizationContext. Use StopAsync() instead. / 호출 스레드를 블로킹하며 SynchronizationContext 환경에서 데드락 위험이 있습니다. StopAsync()를 사용하세요.")]
490 public void Stop()
491 {
492 StopAsync().AsTask().GetAwaiter().GetResult();
493 }
494
519 public async ValueTask StopAsync()
520 {
521 Thread? threadToJoin;
522
523 lock (_syncRoot)
524 {
525 if (Status is DreamineThreadStatus.Stopped or DreamineThreadStatus.Disposed)
526 {
527 return;
528 }
529
530 Status = DreamineThreadStatus.Stopping;
531 _pauseEvent.Set();
532
533 _cancellationTokenSource?.Cancel();
534 threadToJoin = _thread;
535 }
536
537 if (threadToJoin is not null && threadToJoin.IsAlive)
538 {
539 await Task.Run(
540 () => threadToJoin.Join(Options.StopTimeout))
541 .ConfigureAwait(false);
542 }
543
544 lock (_syncRoot)
545 {
546 _cancellationTokenSource?.Dispose();
548 _thread = null;
549
550 if (Status != DreamineThreadStatus.Faulted)
551 {
553 }
554
555 _stoppedAt = DateTimeOffset.Now;
556 }
557
558 _logger?.Info($"Thread stopped. Name={Name}");
559 }
560
578 {
579 return new DreamineThreadInfo(
580 Name,
581 Status,
582 Options.Priority,
583 Options.IntervalMs,
584 CoreAssignment.CoreIndex,
585 CoreAssignment.UseAffinity,
586 JobCount,
587 Interlocked.Read(ref _cycleCount),
591 }
592
609 public void Dispose()
610 {
611 if (_disposed)
612 {
613 return;
614 }
615
616 StopAsync().AsTask().GetAwaiter().GetResult();
617
618 _pauseEvent.Dispose();
619 _disposed = true;
620 Status = DreamineThreadStatus.Disposed;
621 }
622
647 private void Run(CancellationToken cancellationToken)
648 {
649 try
650 {
651 if (Options.UseHighPrecisionTimer)
652 {
654 }
655
656 if (CoreAssignment.UseAffinity && CoreAssignment.CoreIndex is not null)
657 {
658 _affinityService?.ApplyToCurrentThread(CoreAssignment.CoreIndex.Value);
659 }
660
661 while (!cancellationToken.IsCancellationRequested)
662 {
663 _pauseEvent.Wait(cancellationToken);
664
665 ExecuteDueJobs(cancellationToken);
666
667 Interlocked.Increment(ref _cycleCount);
668
669 var context = new DreamineThreadCycleContext(
670 Name,
671 Interlocked.Read(ref _cycleCount),
672 JobCount,
673 CoreAssignment.CoreIndex,
674 CoreAssignment.IsOverflowPolling,
675 DateTimeOffset.Now);
676
677 var delayMs = _cyclePolicy.GetDelayMs(
678 Options,
680 context);
681
682 if (delayMs > 0)
683 {
684 cancellationToken.WaitHandle.WaitOne(delayMs);
685 }
686 else if (Options.YieldWhenIntervalIsZero)
687 {
688 Thread.Yield();
689 }
690 }
691 }
692 catch (OperationCanceledException)
693 {
694 // Normal stop path.
695 }
696 catch (Exception ex)
697 {
698 _lastErrorMessage = ex.Message;
700 _logger?.Error(ex, $"Thread faulted. Name={Name}");
701 }
702 finally
703 {
704 if (Options.UseHighPrecisionTimer)
705 {
707 }
708
709 if (CoreAssignment.UseAffinity)
710 {
711 _affinityService?.ClearCurrentThreadAffinity();
712 }
713 }
714 }
715
748 private void ExecuteDueJobs(CancellationToken cancellationToken)
749 {
750 IDreamineThreadJob[] jobs;
751
752 lock (_syncRoot)
753 {
754 jobs = _jobs.ToArray();
755 }
756
757 var now = DateTimeOffset.Now;
758
759 foreach (var job in jobs)
760 {
761 if (!job.ShouldRun(now))
762 {
763 continue;
764 }
765
766 try
767 {
768 // Jobs run on a dedicated worker thread. Blocking here is
769 // intentional so jobs preserve registration order and the cycle
770 // policy observes completed work before calculating the next delay.
771 job.ExecuteAsync(cancellationToken).GetAwaiter().GetResult();
772 }
773 catch (OperationCanceledException)
774 {
775 throw;
776 }
777 catch (Exception ex)
778 {
779 _lastErrorMessage = ex.Message;
780 _logger?.Error(ex, $"Thread job failed. Thread={Name}, Job={job.Name}");
781 }
782 }
783 }
784
809 private static ThreadPriority MapPriority(DreamineThreadPriority priority)
810 {
811 return priority switch
812 {
813 DreamineThreadPriority.High => ThreadPriority.AboveNormal,
814 DreamineThreadPriority.Low => ThreadPriority.BelowNormal,
815 _ => ThreadPriority.Normal
816 };
817 }
818
835 private void ThrowIfDisposed()
836 {
837 if (_disposed)
838 {
839 throw new ObjectDisposedException(nameof(DreamineThread));
840 }
841 }
842}
DreamineThread(DreamineThreadOptions options, DreamineThreadCoreAssignment coreAssignment, IThreadCyclePolicy cyclePolicy, IThreadAffinityService? affinityService=null, ITimerResolutionService? timerResolutionService=null, IDreamineLogger? logger=null)
readonly IThreadCyclePolicy _cyclePolicy
void Run(CancellationToken cancellationToken)
DreamineThreadCoreAssignment CoreAssignment
readonly List< IDreamineThreadJob > _jobs
readonly? IThreadAffinityService _affinityService
static ThreadPriority MapPriority(DreamineThreadPriority priority)
void ExecuteDueJobs(CancellationToken cancellationToken)
CancellationTokenSource? _cancellationTokenSource
volatile DreamineThreadStatus _status
readonly ManualResetEventSlim _pauseEvent
readonly? ITimerResolutionService _timerResolutionService