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

相关推荐
CSDN_RTKLIB10 小时前
【静态初始化与动态初始化】术语对比
开发语言·c++
天上飞的粉红小猪11 小时前
线程同步与互斥
linux·开发语言·c++
未来之窗软件服务11 小时前
幽冥大陆(八十九 ) 自动化在线打包任意平台软件 —东方仙盟练气期
开发语言·c#·自动化·仙盟创梦ide·东方仙盟·在线打包
Rysxt_11 小时前
鸿蒙开发语言ArkTS:全面解析优缺点与未来前景
开发语言·华为·harmonyos
墨雪不会编程11 小时前
C++【string篇2】:从零基础开始到熟悉使用string类
java·开发语言·c++
kylezhao201911 小时前
第四节、C# 上位机面向对象编程详解(工控硬件继承实战版)
c#·工控上位机
光泽雨11 小时前
ST语言与C#语言数据类型对比详解
开发语言·c#
KevinGuo45711 小时前
Selenium3自动化测试实战——基于python语言
开发语言·python·selenium
Tony Bai12 小时前
Go 考古:Go 官方如何决定支持你的 CPU 和 OS?
开发语言·后端·golang