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

相关推荐
Whisper_Sy8 小时前
Flutter for OpenHarmony移动数据使用监管助手App实战 - 网络状态实现
android·java·开发语言·javascript·网络·flutter·php
Bony-8 小时前
Go语言垃圾回收机制详解与图解
开发语言·后端·golang
hmywillstronger8 小时前
【Rhino】【Python】 查询指定字段并cloud标注
开发语言·python
新缸中之脑8 小时前
Weave.js:开源实时白板库
开发语言·javascript·开源
我能坚持多久8 小时前
D16—C语言内功之数据在内存中的存储
c语言·开发语言
leo__5209 小时前
C#与三菱PLC串口通信源码实现(基于MC协议)
开发语言·c#
二十雨辰9 小时前
[python]-函数
开发语言·python
码农水水9 小时前
中国邮政Java面试被问:容器镜像的多阶段构建和优化
java·linux·开发语言·数据库·mysql·面试·php
福楠9 小时前
C++ STL | map、multimap
c语言·开发语言·数据结构·c++·算法
ytttr8739 小时前
地震数据频率波数域变换与去噪的MATLAB实现
开发语言·matlab