2

我的 C# Windows 服务有以下要求。

  1. 在服务启动时,它从 db 中获取数据集合并将其保存在内存中。
  2. 具有从 3 个不同线程定期执行的业务逻辑。
  3. 每个线程将使用来自步骤 1 中提到的数据集合的不同数据子集执行相同的业务逻辑。每个线程将产生不同的结果集。
  4. 如果数据收集发生任何变化,所有 3 个线程都将定期运行。

当任何客户端调用服务时,服务应该能够返回线程执行的状态。

我知道 C# 有不同的机制来实现定期线程执行。计时器、带睡眠的线程、事件事件等待句柄等,我试图了解哪种线程机制或设计模式最适合此要求?

4

2 回答 2

1

更现代的方法是使用任务,但看看原则

namespace Test {

public class Program {

    public static void Main() {

        System.Threading.Thread main = new System.Threading.Thread(() => new Processor().Startup());
        main.IsBackground = false;
        main.Start();
        System.Console.ReadKey();
    }
}

public class ProcessResult { /* add your result state */ }

public class ProcessState {

    public ProcessResult ProcessResult1 { get; set; }
    public ProcessResult ProcessResult2 { get; set; }
    public ProcessResult ProcessResult3 { get; set; }
    public string State { get; set; }
}

public class Processor {

    private readonly object _Lock = new object();
    private readonly DataFetcher _DataFetcher;
    private ProcessState _ProcessState;

    public Processor() {
        _DataFetcher = new DataFetcher();
        _ProcessState = null;
    }

    public void Startup() {
        _DataFetcher.DataChanged += DataFetcher_DataChanged;
    }

    private void DataFetcher_DataChanged(object sender, DataEventArgs args) => StartProcessingThreads(args.Data);

    private void StartProcessingThreads(string data) {

        lock (_Lock) {
            _ProcessState = new ProcessState() { State = "Starting", ProcessResult1 = null, ProcessResult2 = null, ProcessResult3 = null };

            System.Threading.Thread one = new System.Threading.Thread(() => DoProcess1(data)); // manipulate the data toa subset 
            one.IsBackground = true;
            one.Start();

            System.Threading.Thread two = new System.Threading.Thread(() => DoProcess2(data)); // manipulate the data toa subset 
            two.IsBackground = true;
            two.Start();

            System.Threading.Thread three = new System.Threading.Thread(() => DoProcess3(data)); // manipulate the data toa subset 
            three.IsBackground = true;
            three.Start();
        }
    }

    public ProcessState GetState() => _ProcessState;

    private void DoProcess1(string dataSubset) {
        // do work 
        ProcessResult result = new ProcessResult(); // this object contains the result
        // on completion
        lock (_Lock) {
            _ProcessState = new ProcessState() { State = (_ProcessState.State ?? string.Empty) + ", 1 done", ProcessResult1 = result, ProcessResult2 = _ProcessState?.ProcessResult2, ProcessResult3 = _ProcessState?.ProcessResult3 };
        }
    }

    private void DoProcess2(string dataSubset) {
        // do work 
        ProcessResult result = new ProcessResult(); // this object contains the result
        // on completion
        lock (_Lock) {
            _ProcessState = new ProcessState() { State = (_ProcessState.State ?? string.Empty) + ", 2 done", ProcessResult1 = _ProcessState?.ProcessResult1 , ProcessResult2 = result, ProcessResult3 = _ProcessState?.ProcessResult3 };
        }
    }

    private void DoProcess3(string dataSubset) {
        // do work 
        ProcessResult result = new ProcessResult(); // this object contains the result
        // on completion
        lock (_Lock) {
            _ProcessState = new ProcessState() { State = (_ProcessState.State ?? string.Empty) + ", 3 done", ProcessResult1 = _ProcessState?.ProcessResult1, ProcessResult2 = _ProcessState?.ProcessResult2, ProcessResult3 = result };
        }
    }
}

public class DataEventArgs : System.EventArgs {

    // data here is string, but could be anything -- just think of thread safety when accessing from the 3 processors
    private readonly string _Data;

    public DataEventArgs(string data) {
        _Data = data;
    }

    public string Data => _Data;
}

public class DataFetcher {
    //  watch for data changes and fire when data has changed
    public event System.EventHandler<DataEventArgs> DataChanged;
}

}

于 2020-02-19T09:51:01.723 回答
0

最简单的解决方案是在Task Method()style 中定义调度逻辑,并使用 执行它们Task.Run(),而在主线程中只需使用 等待执行完成Task.WaitAny()。如果任务完成,您可以Task.WaitAny再次调用,但不是完成的任务,而是通过Task.Delay(timeUntilNextSchedule). 这样,任务就不会阻塞主线程,您可以避免为了等待而旋转 CPU。一般来说,您可以避免直接在现代 .NET 中进行管理

根据其他要求,例如标准化错误处理、监控能力、这些计划任务的管理,您还可以依赖更强大的解决方案,例如HangFire

于 2020-02-19T02:58:21.317 回答