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锁效率接近,并可简化代码,避免加锁。

相关推荐
caimouse25 分钟前
MonoDevelop 编译用户应用程序时调用的是哪个编译器
c#
沫璃染墨38 分钟前
从零入门计算机网络系列(十一):HTTP协议进阶——从重定向原理到GET/POST与Fiddler抓包
linux·服务器·开发语言·网络·c++·tcp/ip·html
刚入门的大一新生1 小时前
Linux-线程互斥
java·开发语言
香芋芋圆1 小时前
iD-editor 详解|OSM官方在线编辑器,基于D3.js的轻量地图编辑器
开发语言·javascript·编辑器
小熊恋旧2 小时前
Python 面向对象编程:方法、属性、继承与多态详解
开发语言·python
云边云科技_云网融合2 小时前
面向汽车零部件制造业场景的解决方案 | 70 秒:一台车下线的时间里,藏着五张网
开发语言·汽车·php
Evand J2 小时前
【PMSM滤波例程2】PMSM未知负载转矩观测EKF。MATLAB例程(负载增广EKF与实际仿真结果)。附下载链接
开发语言·matlab·电机
will_net2 小时前
apollo 站点部署&C#接入
开发语言·c#·apollo
晗夜未央3 小时前
任务队列反复失败,先分清重试、阻塞和结果待核对
开发语言·javascript
熊猫钓鱼>_>3 小时前
四种书签,一条时间线:一个读书 App 的记忆颗粒与它的边界案例【鸿蒙心迹】
开发语言·华为·ai编程·harmonyos·鸿蒙·openharmony·鸿蒙pc