65.9K
CodeProject 正在变化。 阅读更多。
Home

CCRing

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (5投票s)

2008年10月29日

CPOL

4分钟阅读

viewsIcon

24633

downloadIcon

131

使用 CCR 的顺序异步日志示例

前言

谈谈同步性,在我发布这篇文章的当天,CCR 和 DSS 成为可下载的产品。你可以在这里找到新产品。我建议你关注这款产品,我怀疑它将成为并发 .NET 软件开发的基石。

引言

我经常重新审视我编写的应用程序,以便利用我随着时间的推移积累的想法和经验来改进代码的某些部分。似乎总有一个主要目标,那就是提高性能。在提高性能方面,你可以做很多事情,但最终你总是会考虑多线程。从理论上讲,这是最简单的建议概念,但并不总是最容易实现的。如果你曾经遇到过资源共享问题,你就会明白我的意思,尽管有很多文章介绍如何解决这个问题,但这并不总是适合所有解决方案。

不久前,我偶然发现了 CCR,它就像两位巫师 Jeff Richter 和 George Chrysanthakopoulos 创建的神奇代码。神奇之处在于能够一口气流畅地念出 Chrysanthakopoulos 的音节,当你克服了这一点后,你就会看到多线程恐怖走廊尽头的光明。这个托管 DLL 充满了大量的多线程乐趣,并为常见的线程复杂性提供了许多简化级别。换句话说,如果你想通过实现多线程层来提高应用程序的性能,那么你需要学习并掌握 CCR。为了获得一些很好的背景知识和乐趣,准备好爆米花,访问 Jeff 和 George 的链接。

背景

观看视频播客后,你应该会充满信心和启发,并有勇气开始使用 CCR。所以你会打开你最新的项目,然后……从哪里开始呢?一个起点是创建一个简单的异步日志记录器。我设计的大多数应用程序都具有不同级别的日志记录用于生产诊断,但是如果你在使用日志记录类时没有使用线程模型,那么你就创建了阻塞代码,并且显然有改进的空间。因此,为了帮助你入门,我将向你展示如何实现一个使用 CCR 的日志记录类,该类写入本地文件。有很多方法可以记录数据,但在这个演示中我使用的是简单的本地日志记录。你很可能会对这篇文章感兴趣文章;它将解释 CCR 的多种形式。

Using the Code

下面的代码可以直接添加到你的应用程序中并立即使用,虽然它很简单,但它可以替代你当前实现的任何日志记录方法。

首先,我们需要创建一个名为 `Dispatcher` 的对象,可以将其视为线程“池”。注意“`1`”,这意味着我们只希望一个线程处理这些调用,因此对该类所有“发布”的操作都将异步但顺序执行。如果你正在写入 SQL 数据库,你可以尝试增加此数字,但请注意数据可能不会按顺序到达!当对其他非顺序任务使用调度程序时,尝试增加此数字。

//Create Dispatcher
private static readonly Dispatcher _logDispatcher = new Dispatcher(1, "LogPool");

其次,你需要一个 `DispatcherQueue`。DQ 管理你的方法委托列表,即你需要在需要时执行的方法。

//Create Dispatch Queue
private static DispatcherQueue _logDispatcherQueue;

接下来你需要一个 PORT,端口就像输入队列。你将“发布”到端口以调用你注册的方法。

//Message Port
private static readonly Port<string> _logPort = new Port<string>();

现在是类,别忘了在指令中包含 CCR!

using System;
using System.IO;
using System.Threading;
using Microsoft.Ccr.Core;

namespace CCRing_Demo
{
    public static class DataLogger
    {
        //Create Dispatcher
        private static readonly Dispatcher _logDispatcher = new Dispatcher(1,
            ThreadPriority.Normal, false, "LogPool");

        //Create Dispatch Queue
        private static DispatcherQueue _logDispatcherQueue;

        //Message Port
        private static readonly Port<string> _logPort = new Port<string>();

        //Fields
        private static string _logFileName;

        private static void Init()
        {
            _logDispatcherQueue = new DispatcherQueue("LogDispatcherQueue",
                _logDispatcher);
            Arbiter.Activate(_logDispatcherQueue, Arbiter.Receive(true, _logPort,
                WriteMessage));

            _logFileName = "DMT_Message_Log_" + String.Format("{0:yyMMddHHmmss}",
                DateTime.Now) + ".log";
        }

        private static void WriteMessage(string messageString)
        {
            using (var sw = File.AppendText(_logFileName))
                sw.WriteLine("[{0:HH:mm:ss tt}] {1}", DateTime.Now, messageString);
        }

        public static void Log(string messageString)
        {
            if (String.IsNullOrEmpty(_logFileName))
                Init();
            _logPort.Post(messageString);
        }

        //Any thread tasks still running?
        private static bool PendingJobs
        {
            get 
            {
                return (_logDispatcher.PendingTaskCount > 0 || 
				_logPort.ItemCount > 0) ? true : false;            
            }
        }

        //Since we are not using background threading we need to add this method to
        //dispose the DQ for application end
        public static void StopLogger()
        {
            while (PendingJobs){Thread.Sleep(100);}
            _logFileName = null;
            _logDispatcherQueue.Dispose();
        }
    }
}

关注点

最新版本的 Visual Studio 中不包含 CCR。它是 Microsoft Robotics Studio 的一部分,但是无需下载整个工作室,我已经在上面包含了 DLL,因此你可以将其作为引用添加到你的项目中。

你应该注意到的一个代码特点是这里缺乏回调嵌套,这是一个非常好的模型。

此外,如果你使用后台线程,你的主/初始线程将等待 `Dispatcher`,即使所有已排队的发布都已完成。你可以通过多种方法来处理这个问题,例如在不使用后台线程的情况下创建 `Dispatcher`,但在这种情况下,你需要使用 `PendingJobs` 来确保所有作业都已完成。

虽然这个类的设计和用途相当简单,但你至少应该看到 CCR 只需几行代码就能发挥的强大功能。逐步执行代码并添加一些额外的端口以获得乐趣。你对 CCR 理解得越多,你就会越了解它如何从现在开始改进你编写的几乎任何应用程序。

快乐 CCRing!

历史

  • 2008 年 10 月 29 日:首次发布
© . All rights reserved.