如何使异步事件连续运行?

时间:2017-11-16 04:46:07

标签: c# .net async-await

我正在创建一个仅在概念验证阶段的小型控制台应用程序。我们的想法是,它将查询第三方API的最终事件。 (那部分工作得很好)。

当一个事件到来时,我们需要将该事件的json字符串粘贴到我们这边的sqlite数据库中。同时,我需要一个在同一个应用程序中运行的进程,该进程在sqlite表中不断查找记录,当它找到一个时,它会处理它。

这是我到目前为止所拥有的......(同样,这只是一个概念证明,同时我学习了如何设置它的基础知识)......

static void Main(string[] args)
{
    var t = CheckEventsAsync();
    while (true)
    {
        System.Threading.Thread.Sleep(5000);
        DataAccess.InsertData("INSERT INTO events(value) VALUES ('this is an event');");
    }
}

static private async Task CheckEventsAsync()
{
    DataTable dt = await DataAccess.LoadDataAsync("SELECT * FROM events");
    foreach (var dr in dt.Rows)
    {
        Console.WriteLine("got an event!");
    }
}

问题是CheckEventsAsync()只触发一次,然后当5秒计时器关闭时,一条记录被添加到表中,没有任何反应。

我是否需要使用EventHandler(使用委托等)来完成这项工作?或者可能不这样做吗?

谢谢!

哦,差点忘了......(这是因为如果有数据则返回数据表,如果没有则返回null。但我不确定我是否正确地为整体目标做准备)...

    public static async Task<DataTable> LoadDataAsync(string txtQuery)
    {
        var dataTable = new DataTable();
        var connection = new SQLiteConnection(_source);
        using (connection)
        {
            connection.Open();
            var command = connection.CreateCommand();
            command.CommandText = txtQuery;
            var task = await command.ExecuteReaderAsync();
            dataTable = new DataTable();
            dataTable.Load(task);
            connection.Close();
        }
        if (dataTable != null & dataTable.Rows.Count > 0)
            return dataTable;
        else
            return null;
    }

修改

到目前为止,我从答案中看到,我在提问时做得不好。我需要运行两个进程:

  1. 第一个进程将侦听第三方事件并将其添加到sqlite表中。这是用5秒计时器模拟的。但实际上,While(true)循环内不会有计时器。它将是一个WebSocket侦听器。
  2. 第二个进程将监视数据库中的记录并处理它们。因此,CheckEventsAsync()需要是一个在没有调用方法的情况下触发的侦听器。
  3. 两者需要在同一个应用程序中运行,但彼此独立。

    我希望我有意义吗?

3 个答案:

答案 0 :(得分:4)

使用Microsoft的Reactive Framework:

static readonly BlockingCollection<ItemToProcess> _queue = new BlockingCollection<ItemToProcess>(new ConcurrentQueue<ItemToProcess>());

static void Main(string[] args)
{
    IDisposable inserts =
        Observable
            .Interval(TimeSpan.FromSeconds(5.0))
            .Subscribe(_ =>
            {
                _queue.Add(new ItemToProcess() { Value = "value" });
            });

    IDisposable checks =
        new CompositeDisposable(
            Disposable.Create(() => _queue.CompleteAdding()),
            _queue
                .GetConsumingEnumerable()
                .ToObservable(Scheduler.Default)
                .Subscribe(item =>
                {
                    Console.WriteLine(item.Value);
                }));
}

这可以处理所有线程,并可以轻松关闭应用程序。只需在退出前致电inserts.Dispose()checks.Dispose()

NuGet“System.Reactive”获取位。

答案 1 :(得分:3)

由于所有操作都在同一个操作系统进程中运行 - 您不需要不断地为新事件轮询数据库。相反 - 使用内存队列,所有生产者都会将数据和消费者推送到这个队列中。您仍然可以将数据插入到sqlite表中以处理进程崩溃并且某些项目未处理的情况。以下是一些代码(出于说明目的 - 在您的实际应用程序中,您将不会有Thread.Sleep当然这样):

class Program {
    static readonly BlockingCollection<ItemToProcess> _queue = new BlockingCollection<ItemToProcess>(new ConcurrentQueue<ItemToProcess>());
    static readonly CancellationTokenSource _cts = new CancellationTokenSource();

    static void Main(string[] args) {
        // get all unprocessed items from database here and insert into queue
        var producerThread = StartProducer(_cts.Token);
        var consumerThread = StartConsumer();
        Console.ReadKey();
        // cancel
        _cts.Cancel();
        // wait for producer thread to finish
        producerThread.Join();
        // notify there will be no more items
        _queue.CompleteAdding();
        // wait for consumer to finish
        consumerThread.Join();
    }

    static Thread StartProducer(CancellationToken ct) {
        var thread = new Thread(() => {
            while (!ct.IsCancellationRequested) {                    
                // also insert into database here
                _queue.Add(new ItemToProcess() {
                    Value = "value"
                });
                // imitate delay
                Thread.Sleep(5000);
            }
        }) {
            IsBackground = true
        };
        thread.Start();
        return thread;
    }

    static Thread StartConsumer() {
        var thread = new Thread(() => {
            foreach (var item in _queue.GetConsumingEnumerable()) {
                try {
                    // process
                    Console.WriteLine(item.Value);
                    // delete from database here
                }
                catch (Exception ex) {
                    // handle
                }
            }
        }) {
            IsBackground = true
        };
        thread.Start();
        return thread;
    }
}

class ItemToProcess
{
    public string Value { get; set; }
}

答案 2 :(得分:0)

你的任务超出了五秒循环,所以它当然只会触发一次。