c# - 线程间通信时间

标签 c# messaging ccr

我正在通过端口和接收器将 15 个异步操作链接在一起。这让我非常关心线程间消息传递时间,特别是任务将数据发布到端口与新任务开始在不同线程上处理相同数据之间所花费的时间。假设最好的情况是每个线程在开始时都是空闲的,我生成了一个测试,它使用秒表类来测量两个不同的调度程序的时间,每个调度程序都以最高优先级运行一个线程。

我的发现让我感到惊讶,我的开发平台是运行 Windows 7 x64 的 Q6600 四核 2.4 Ghz 计算机,我测试的平均上下文切换时间为 5.66 微秒,标准偏差为 5.738 微秒,最大值接近1.58 毫秒(282 倍!)。秒表频率为 427.7 纳秒,所以我仍然没有受到传感器噪音的影响。

我想做的是尽可能减少线程间消息传递时间,同样重要的是,减少上下文切换的标准偏差。我意识到 Windows 不是实时操作系统,也没有任何保证,但 Windows 调度程序是一个基于公平循环优先级的调度程序,并且此测试中的两个线程都处于最高优先级(唯一应该是的线程高),所以线程上不应该有任何上下文切换(由 1.58 毫秒的最大时间证明......我相信 Windows 量子是 15.65 毫秒?)我唯一能想到的是操作系统调用时间的变化CCR 用于在线程之间传递消息的锁定机制。

请让我知道是否有其他人测量过线程间消息传递时间,并对如何改进它有任何建议。

这是我测试的源代码:

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

using System.Diagnostics;

namespace Test.CCR.TestConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Starting Timer");
            var sw = new Stopwatch();
            sw.Start();

            var dispatcher = new Dispatcher(1, ThreadPriority.Highest, true, "My Thread Pool");
            var dispQueue = new DispatcherQueue("Disp Queue", dispatcher);

            var sDispatcher = new Dispatcher(1, ThreadPriority.Highest, true, "Second Dispatcher");
            var sDispQueue = new DispatcherQueue("Second Queue", sDispatcher);

            var legAPort = new Port<EmptyValue>();
            var legBPort = new Port<TimeSpan>();

            var distances = new List<double>();

            long totalTicks = 0;

            while (sw.Elapsed.TotalMilliseconds < 5000) ;

            int runCnt = 100000;
            int offset = 1000;

            Arbiter.Activate(dispQueue, Arbiter.Receive(true, legAPort, i =>
                                                                            {
                                                                                TimeSpan sTime = sw.Elapsed;
                                                                                legBPort.Post(sTime);
                                                                            }));
            Arbiter.Activate(sDispQueue, Arbiter.Receive(true, legBPort, i =>
                                                                             {
                                                                                 TimeSpan eTime = sw.Elapsed;
                                                                                 TimeSpan dt = eTime.Subtract(i);
                                                                                 //if (distances.Count == 0 || Math.Abs(distances[distances.Count - 1] - dt.TotalMilliseconds) / distances[distances.Count - 1] > 0.1)
                                                                                 distances.Add(dt.TotalMilliseconds);

                                                                                 if(distances.Count > offset)
                                                                                 Interlocked.Add(ref totalTicks,
                                                                                                 dt.Ticks);
                                                                                 if(distances.Count < runCnt)
                                                                                     legAPort.Post(EmptyValue.SharedInstance);
                                                                             }));


            //Thread.Sleep(100);
            legAPort.Post(EmptyValue.SharedInstance);

            Thread.Sleep(500);

            while (distances.Count < runCnt)
                Thread.Sleep(25);

            TimeSpan exTime = TimeSpan.FromTicks(totalTicks);
            double exMS = exTime.TotalMilliseconds / (runCnt - offset);

            Console.WriteLine("Exchange Time: {0} Stopwatch Resolution: {1}", exMS, Stopwatch.Frequency);

            using(var stw = new StreamWriter("test.csv"))
            {
                for(int ix=0; ix < distances.Count; ix++)
                {
                    stw.WriteLine("{0},{1}", ix, distances[ix]);
                }
                stw.Flush();
            }

            Console.ReadKey();
        }
    }
}

最佳答案

Windows 不是实时操作系统。但是你已经知道了。杀死你的是上下文切换时间,不一定是消息时间。您并没有真正指定进程间通信的工作方式。如果您真的只是运行多个线程,您会发现不使用 Windows 消息作为通信协议(protocol)会有所收获,而是尝试使用应用程序托管的消息队列来滚动您自己的 IPC。

在任何版本的 Windows 中发生上下文切换时,您可以期望的最佳平均值是 1 毫秒。当您的应用程序必须屈服于内核时,您可能会看到 1 毫秒的时间。这是为 Ring-1 应用程序(用户空间)设计的。如果低于 1 毫秒绝对至关重要,则需要将部分应用程序切换到 Ring-0,这意味着编写设备驱动程序。

设备驱动程序不会像用户应用程序那样遭受相同的上下文切换时间,并且还可以访问纳秒分辨率计时器和 sleep 调用。如果您确实需要这样做,可以从 Microsoft 免费获得 DDK(设备驱动程序开发工具包),但我强烈建议您投资购买第 3 方开发工具包。他们通常有非常好的示例和大量向导来正确设置,这可能需要您阅读 DDK 文档数月才能发现。您还需要 SoftIce 之类的东西,因为普通的 Visual Studio 调试器不会帮助您调试设备驱动程序。

关于c# - 线程间通信时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1366112/

相关文章:

data-structures - Disruptor 模式和 NServiceBus

java - JMS(或任何消息传递解决方案)是否适合追随者/追随者模型

c# - 如何重放已通过Azure服务总线处理过的消息?

c# - 找不到我的 Entity Framework 数据库

multithreading - 异步 F# 与 CCR 框架

ado.net - 微软机器人和 Sql

c# - 在 WPF 中,由于空引用导致绑定(bind)失败时是否使用 FallbackValue?

C# 异步方法仍然挂起 UI

c# - Windows Phone 使 Segoe UI 符号不显示为表情符号