【问题标题】:Ordering of Asynchronous method calls异步方法调用的顺序
【发布时间】:2011-08-18 21:54:08
【问题描述】:

我正在开发一个类库,该类库根据 Web 配置文件中定义的策略在多种类型的数据源(文件、xml、数据库)中记录 Web 应用程序的审核详细信息。

我的审计日志方法有一个类似这样的签名: public static void LogInfo(User user, Module module, List lst);

Web 应用程序使用此方法记录重要的详细信息,例如警告、错误甚至异常详细信息。

由于在单个工作流程中,对这些方法的调用超过 700 次,我想将它们设为异步。我使用了 ThreadPool 类中的简单方法,名为 QueueUserWorkItem

ThreadPool.QueueUserWorkItem(o => LogInfo(User user, Module module, List<Object> lst) );

但这并不能确保工作项排队的顺序。即使我的所有信息都被记录了,但整个订单却一团糟。在我的文本文件中,我的日志不是按照它们被调用的顺序。

有没有一种方法可以控制使用 QueueUserWorkItem 调用的线程的顺序?

【问题讨论】:

    标签: c# asp.net threadpool


    【解决方案1】:

    我认为您在使用QueueUserWorkItem 时不能指定排序。

    要并行运行日志记录(在某个后台线程上),您可以使用ConcurrentQueue&lt;T&gt;。这是一个线程安全的集合,可以从多个线程访问。您可以创建一个从集合中读取元素并将它们写入文件的工作项(或线程)。您的主应用程序会将项目添加到集合中。您从单个线程将项目添加到集合中的事实应保证它们将以正确的顺序被读取。

    为简单起见,您可以将 Action 值存储在队列中:

    ConcurrentQueue<Action> logOperations = new ConcurrentQueue<Action>();
    
    // To add logging operation from main thread:
    logOperations.Add(() => LogInfo(user, module, lst));
    

    后台任务可以从队列中取出Actions 并运行它们:

    // Start this when you create the `logOperations` collection
    ThreadPool.QueueUserWorkItem(o => {
        Action op;
        // Repeatedly take log operations & run them
        while (logOperations.TryDequeue(out op)) op();
    });
    

    如果您需要停止后台处理器(将数据写入日志),您可以创建一个CancellationTokenSource 并在取消令牌时(由主线程)结束while 循环。使用 IsCancellationRequested 属性 (see MSDN) 来检查这个 cha

    【讨论】:

    • EDIT 我最初建议使用BlockingCollection,但在这种情况下使用ConcurrentQueue 就足够了。 (实际上,我什至在看到 Brizio 的回答之前就意识到了这一点 :-))
    • 我将从页面的生命周期事件中多次调用 Audit 方法。事实上,它也可以从一个方法中多次调用。 ConcurrentQueue 可以解决这个问题吗?
    • @Kunal:是的,您可以添加多个操作(通过反复调用Add)。
    • 你应该重新考虑BlockingCollectionConcurrentQueue.TryDequeue 将在队列第一次变空时返回 false,这将导致您的线程退出。您可以编写等待项目等的逻辑,也可以只使用BlockingCollectionConcurrentQueue.TryDequeue 不支持取消。为此,您需要 BlockingCollection
    • @Jim:是的,我认为你是对的——使用BlockingCollection 可能是一个更好的主意! (你不需要在生产者端阻塞,但是取消是非常有用的)。
    【解决方案2】:

    解决此问题的一种方法是将数据放入队列中,然后从该队列中挑选一个任务并按顺序写入它们。如果您使用的是 .net 4.0,您可以使用线程安全的 ConcurrentQueue,否则带有适当锁的简单队列也可以工作。

    然后,使用队列的线程可以定期检查队列中的任何元素,并记录每个元素。这样,冗长的操作(记录)可以在它自己的线程中,而在主线程中您只需添加。

    【讨论】:

      猜你喜欢
      • 2012-03-15
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多