对于异步执行方法,还有什么比BlockingCollection更好的呢?

本文关键字:什么 BlockingCollection 更好 于异步 异步 方法 执行 | 更新日期: 2023-09-27 18:31:58

我写了这样一个例子来衡量异步执行的阻塞集合有多快

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace TestBlockingCollection
{
    class Program
    {
        static void Main(string[] args)
        {
            BlockingCollection<int> blockingCollection = new BlockingCollection<int>();
            Stopwatch sw = Stopwatch.StartNew();
            Task.Factory.StartNew(() =>
            {
                int i = 0;
                while (true)
                {
                    Console.WriteLine("Adding " + i);
                    sw = Stopwatch.StartNew();
                    blockingCollection.Add(i++);
                    Thread.Sleep(1000);
                }
            });
            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    int i = blockingCollection.Take();
                    sw.Stop();
                    long microseconds = sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
                    Console.WriteLine("Received " + i + ". Spent " + microseconds + " microseconds.");
                }
            });
            while(true)
            {
                Thread.Sleep(1000);
            }
        }
    }
}

结果令人失望:

Adding 0
Received 0. Spent 19593 microseconds.
Adding 1
Received 1. Spent 220 microseconds.
Adding 2
Received 2. Spent 38 microseconds.
Adding 3
Received 3. Spent 104 microseconds.
Adding 4
Received 4. Spent 46 microseconds.
Adding 5
Received 5. Spent 37 microseconds.
Adding 6
Received 6. Spent 112 microseconds.
Adding 7
Received 7. Spent 103 microseconds.
Adding 8
Received 8. Spent 104 microseconds.
Adding 9
Received 9. Spent 384 microseconds.
Adding 10
Received 10. Spent 102 microseconds.
Adding 11
Received 11. Spent 39 microseconds.
Adding 12
Received 12. Spent 51 microseconds.
Adding 13
Received 13. Spent 42 microseconds.
Adding 14
Received 14. Spent 40 microseconds.
Adding 15
Received 15. Spent 40 microseconds.
Adding 16
Received 16. Spent 42 microseconds.
Adding 17
Received 17. Spent 40 microseconds.
Adding 18
Received 18. Spent 41 microseconds.
Adding 19
Received 19. Spent 42 microseconds.
Adding 20
Received 20. Spent 62 microseconds.
Adding 21
Received 21. Spent 36 microseconds.
Adding 22
Received 22. Spent 39 microseconds.
Adding 23
Received 23. Spent 35 microseconds.
Adding 24
Received 24. Spent 40 microseconds.
Adding 25
Received 25. Spent 63 microseconds.
Adding 26
Received 26. Spent 56 microseconds.
Adding 27
Received 27. Spent 42 microseconds.
Adding 28
Received 28. Spent 41 microseconds.
Adding 29
Received 29. Spent 42 microseconds.
Adding 30
Received 30. Spent 41 microseconds.
Adding 31
Received 31. Spent 651 microseconds.
Adding 32
Received 32. Spent 43 microseconds.
Adding 33
Received 33. Spent 58 microseconds.
Adding 34
Received 34. Spent 43 microseconds.
Adding 35
Received 35. Spent 41 microseconds.
Adding 36
Received 36. Spent 59 microseconds.
Adding 37
Received 37. Spent 38 microseconds.
Adding 38
Received 38. Spent 38 microseconds.
Adding 39
Received 39. Spent 38 microseconds.
Adding 40
Received 40. Spent 42 microseconds.
Adding 41
Received 41. Spent 59 microseconds.
Adding 42
Received 42. Spent 40 microseconds.
Adding 43
Received 43. Spent 42 microseconds.
Adding 44
Received 44. Spent 41 microseconds.
Adding 45
Received 45. Spent 39 microseconds.
Adding 46
Received 46. Spent 42 microseconds.
Adding 47
Received 47. Spent 41 microseconds.
Adding 48
Received 48. Spent 41 microseconds.
Adding 49
Received 49. Spent 42 microseconds.
Adding 50
Received 50. Spent 35 microseconds.
Adding 51
Received 51. Spent 42 microseconds.
Adding 52
Received 52. Spent 39 microseconds.
Adding 53
Received 53. Spent 43 microseconds.
Adding 54
Received 54. Spent 35 microseconds.
Adding 55
Received 55. Spent 60 microseconds.
Adding 56
Received 56. Spent 59 microseconds.
Adding 57
Received 57. Spent 55 microseconds.
Adding 58
Received 58. Spent 74 microseconds.
Adding 59
Received 59. Spent 56 microseconds.
Adding 60
Received 60. Spent 42 microseconds.

我平均花费了大约 50 微秒,但有时我花费了高达 600 微秒!

即使使用我的慢速奔腾 U5400,我也希望它应该是几个常数,不超过 10 微秒,也不会超过 10 微秒。

.NET 对异步执行有什么更快的方法?在安排异步执行后,我需要它尽快启动。这是金融上的时间敏感计算。

阻止收集保证订单和保证项目将逐个处理,所以这个问题实际上包含两个问题

  1. 如果我需要订单并且我需要按照出现的顺序处理项目,我们是否有更快的东西? 即我需要先进先出查询。
  2. 如果我不关心订单,我不关心项目是逐个处理还是并行处理,我们是否有更快的速度?

我想答案是:

  1. 不。我必须使用 BlockingCollection 来引用使用 BlockingCollection 作为单一生产者、单一消费者 FIFO 查询是否好?

  2. 我大概可以试试代表? http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx

对于异步执行方法,还有什么比BlockingCollection更好的呢?

经过几个小时的实验,我认为是测量成为障碍;我将其更改为运行 10000,结果为:

100000 次迭代总计 214.0,平均值为 0.00214 ms

代码与您的代码几乎相同(下面供参考);我确实做了一个发布版本

我尝试使用屏障,但它更慢。我也尝试仅使用锁来做到这一点,但未能使其正常工作。

   static void Main(string[] args)
    {
        var barrier = new Barrier(2);
        var collection = new List<int>();
        var num_iterations = 100000;
        var iterations = num_iterations;
        var total_time_ms = 0.0M;
        Stopwatch sw = new Stopwatch();

        total_time_ms = 0.0M;
        iterations = num_iterations;
        var blockingCollection = new BlockingCollection<int>();
        Task.Factory.StartNew(() =>
        {
            int i = 0;
            while (iterations-- > 0)
            {
                sw.Restart();
                blockingCollection.Add(i++);
            }
        });

        Task.Factory.StartNew(() =>
        {
            var expected_value = 0;
            while (iterations > 0) // stop when performed certain number
            {
                int i = blockingCollection.Take();
                sw.Stop();
                long microseconds = sw.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
                total_time_ms += microseconds;
                if (i != expected_value)
                    Console.WriteLine(String.Format("** expected {0} got {1}", i, expected_value));
                expected_value++;
            }
        });
        while (iterations > 0)
        {
            Thread.Sleep(1000);
        }
        Console.WriteLine(String.Format("Total {0} for {1} iterations, average {2}", total_time_ms, num_iterations, total_time_ms / num_iterations));

我相信BlockingCollection是一个很好的集合。还有其他方法可以做到这一点,但这是一个复杂的领域,不太可能获得比这更快的东西。

首先,如果你真的关心微秒,也许你不应该使用.Net(因为它的垃圾收集器会给你不可预测的延迟)或Windows(因为它不是实时操作系统)。

要实际回答您的问题:

  1. 我认为 50 微秒是一个非常短的时间,而且BlockingCollection<T>是高度优化的,所以我认为你无法更快地做到这一点。您可以使用旋转锁来做到这一点,但代价是浪费 CPU 时间什么都不做。

  2. 异步调用委托几乎与使用 ThreadPool.QueueUserWorkItem() 相同。所以,是的,这可能接近你能得到的最好的。尽管同步调用总是会更快。