如何创建基于Task / TPL的工作队列/事件循环?

时间:2016-05-15 04:48:15

标签: c# multithreading xamarin

我有一个系统,所有数据库操作(SQLite)都必须通过使用工作处理器来运行,这样我就可以安排它们了。

现在处理器有一个Task<TResult> PostAsync<TResult>(Func<SQLiteConnection, TResult> dbFunc),但我能够提出的唯一理智的实现是一个基于堆栈的UI线程工作循环,利用Task能够推迟结果直到处理实际行动的那一刻。

它运行正常,但它正在用SQLite调用堵塞UI线程。我宁愿让这件事没有发生,但我能想到的唯一想法是产生一个工作线程来刷新队列或给每个动作它自己的工作线程。我不认为其中任何一个是最理智的方法,因为他们每次需要DB访问时最终都会创建线程。

如何实现一个实现,其中堆栈中的dbFunc参数在后台线程中执行,但所述线程只创建一次或尽可能轻量级?

这必须在Xamarin环境中工作。在PCL或Android + iOS实现中。

1 个答案:

答案 0 :(得分:1)

您不需要泵或队列类型的解决方案来实现此目的。只需通过SQLiteConnection序列化对SemaphoreSlim的访问权限即可(当然您需要单例SQLiteConnectionSemaphoreSlim个实例):

private readonly SQLiteConnection _sharedConnection;
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1);

public async Task<TResult> PostAsync<TResult>(Func<SQLiteConnection, TResult> dbFunc, CancellationToken ct)
{
    TResult result;
    bool needToRelease = false;

    try
    {
        await Semaphore.WaitAsync(ct).ConfigureAwait(false);

        // If we got this far, Release *must* be called.
        needToRelease = true;

        ct.ThrowIfCancellationRequested();

        // Push the work off to the thread pool in the event that
        // WaitAsync completed synchronously and we're still on the UI thread.
        result = await Task
            .Run(() => dbFunc(_sharedConnection), ct)
            .ConfigureAwait(false);
    }
    finally
    {
        if (needToRelease) {
            Semaphore.Release();
        }
    }

    return result;
}

以上内容适用于Xamarin.iOS和Xamarin.Android - 但我了解SemaphoreSlim在某些PCL配置文件中不可用。

您可能会认为每次需要访问数据库时启动多个Task实例都有点丰富,但实际上即使对于移动设备也没有太多工作。

上面一个更大的问题是它仍然是假的&#34;异步。我们只是滥用Task.Run将阻塞操作卸载到线程池。

因此,一个更基本的替代方案(I myself use)值得一提。在执行SQLiteConnection实例上的相关方法之前,通过包装类公开您的lock,其中每个公共方法SQLiteConnection都在共享对象上。在这种情况下,调用者是否要直接执行可能阻塞的调用,或者通过Task.Run将它们推送到线程池。在SQLite.Net开始提供 true 异步之前,我相信这是使用SQLiteConnection管理并发的最佳方法。