C#调用F#的MailboxProcessor

MailboxProcessor是单线程的简化Actor实现,在F#的dll类库中定义,C#中使用。

F#的库,需要从nuget下载FSharp.Core

cs 复制代码
type CounterMessage = 
    | Increment of int 
    | GetCount of AsyncReplyChannel<int>

type CounterMailbox() = 
    let agent = MailboxProcessor<CounterMessage>.Start(fun inbox -> 
        let rec loop count = async { 
            let! msg = inbox.Receive() 
            match msg with 
            | Increment value -> return! loop (count + value) 
            | GetCount replyChannel -> replyChannel.Reply count 
            return! loop count 
        } 
        loop 0)
    member this.Increment(value) = agent.Post(Increment value)
    member this.GetCount() = agent.PostAndReply(GetCount)
    member this.GetCountAsync() = Async.StartAsTask(agent.PostAndAsyncReply(GetCount))
cs 复制代码
        private CounterMailbox _counterMailbox;
        private void Form1_Load(object sender, EventArgs e)
        {
            var myClass = new MyClass();
            myClass.MyMethod();
            _counterMailbox = new CounterMailbox();
        }

       private void button1_Click(object sender, EventArgs e)
       {
           _counterMailbox.Increment(1);
       }

       private async void button2_Click(object sender, EventArgs e)
       {
           var value = _counterMailbox.GetCount();
           MessageBox.Show(value.ToString());
       }

       private async void button3_Click(object sender, EventArgs e)
       {
           var value = await _counterMailbox.GetCountAsync();
           MessageBox.Show(value.ToString());
       }

       private int _cnt = 0;
       private object _cntLock = new object();
       private async void button4_Click(object sender, EventArgs e)
       {
           var stop = new Stopwatch();
           stop.Start();
           var taskList = new List<Task>();
           for (var i = 0; i < 1000; i++)
           {
               var task = Task.Run(() => {
                   for (var j = 0; j < 10000; j++)
                   {
                       lock (_cntLock)
                       {
                           _cnt++;
                       }
                   }
               });
               taskList.Add(task);
           }
           await Task.WhenAll(taskList);
           stop.Stop();
           MessageBox.Show($"{stop.ElapsedMilliseconds}ms  cnt={_cnt}");
       }

        private async void button5_Click(object sender, EventArgs e)
        {
            var stop = new Stopwatch();
            stop.Start();
            var taskList = new List<Task>();
            for (var i = 0; i < 1000; i++)
            {
                var task = Task.Run(() => {
                    for (var j = 0; j < 10000; j++)
                    {
                        _counterMailbox.Increment(1);
                    }
                });
                taskList.Add(task);
            }
           
            await Task.WhenAll(taskList);
            var cnt = _counterMailbox.GetCount();
            stop.Stop();
            //Thread.Sleep(2000);
            MessageBox.Show($"{stop.ElapsedMilliseconds}ms  cnt={cnt}");
        }

 private ConcurrentQueue<int> _incQueue = new ConcurrentQueue<int>();
 private long _cntV3 = 0;

 private async void button6_Click(object sender, EventArgs e)
 {
     CreateV3IncThread();
     var stop = new Stopwatch();
     stop.Start();
     var taskList = new List<Task>();
     for (var i = 0; i < 1000; i++)
     {
         var task = Task.Run(() => {
             for (var j = 0; j < 10; j++)
             {
                 _incQueue.Enqueue(1);
             }
         });
         taskList.Add(task);
     }

     await Task.WhenAll(taskList);
     var cnt = Interlocked.Read(ref _cntV3);
     stop.Stop();
     //Thread.Sleep(2000);
     MessageBox.Show($"{stop.ElapsedMilliseconds}ms  cnt={cnt}");
 }

 private Thread _v3Thread = null;
 private void CreateV3IncThread()
 {
     if (_v3Thread != null)
         return;
     _v3Thread = new Thread(() =>
     {
         while (true)
         {
             if(_incQueue.TryDequeue(out int i))
                 Interlocked.Add(ref _cntV3, i);
             else
                 Thread.Sleep(1);
         }
     })
     { IsBackground = true};
     _v3Thread.Start();
 }

耗时统计:

|----|----------|------|-------|---------|
| 序号 | 模式 | 线程数 | 线程内循环 | 耗时 |
| 1 | C# lock锁 | 1000 | 10000 | 240MS |
| 2 | Mailbox | 1000 | 10000 | 11117MS |
| 3 | 生产者消费者 | 1000 | 10000 | 1058MS |
| 4 | C# lock锁 | 1000 | 1000 | 26MS |
| 5 | Mailbox | 1000 | 1000 | 1082MS |
| 6 | 生产者消费者 | 1000 | 1000 | 82MS |
| 7 | C# lock锁 | 1000 | 100 | 5MS |
| 8 | Mailbox | 1000 | 100 | 95MS |
| 9 | 生产者消费者 | 1000 | 100 | 8MS |
| 10 | C# lock锁 | 1000 | 10 | 5MS |
| 11 | Mailbox | 1000 | 10 | 5MS |
| 12 | 生产者消费者 | 1000 | 10 | 3MS |

单线程的Actor模型在极高频更新情况下没有优势,单线程递归出现大量积压;在高并发低更新频率下和lock锁效率接近,并可简化代码,避免加锁。

相关推荐
玄同7651 小时前
从 0 到 1:用 Python 开发 MCP 工具,让 AI 智能体拥有 “超能力”
开发语言·人工智能·python·agent·ai编程·mcp·trae
czy87874751 小时前
深入了解 C++ 中的 `std::bind` 函数
开发语言·c++
消失的旧时光-19431 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed
yq1982043011562 小时前
静思书屋:基于Java Web技术栈构建高性能图书信息平台实践
java·开发语言·前端
一个public的class2 小时前
你在浏览器输入一个网址,到底发生了什么?
java·开发语言·javascript
Jinkxs2 小时前
Gradle - 与Groovy/Kotlin DSL对比 构建脚本语言选择指南
android·开发语言·kotlin
&有梦想的咸鱼&2 小时前
Kotlin委托机制的底层实现深度解析(74)
android·开发语言·kotlin
BD_Marathon2 小时前
设计模式——依赖倒转原则
java·开发语言·设计模式
devmoon2 小时前
在 Polkadot Runtime 中添加多个 Pallet 实例实战指南
java·开发语言·数据库·web3·区块链·波卡
Evand J2 小时前
TDOA(到达时间差)的GDOP和CRLB计算的MATLAB例程,论文复现,附参考文献。GDOP:几何精度因子&CRLB:克拉美罗下界
开发语言·matlab·tdoa·crlb·gdop