Excel的线程 与 SynchronizationContext的实现

北城以北 提交于 2021-01-07 08:56:58

COM组件的线程模型与Excel多线程的背景知识

COM组件的线程模型被称之为Apartment模型,COM对象初始化时其执行上下文(Execution Context),他要么和单个线程关联STA(Single Thread Apartment ) 要么和多个线程关联MTA(Multi Thread Apartment)。Excel是一种STA线程的应用程序,使用多线程直接怼会出问题,必须借助线程的同步上下文实现线程间的消息传递。

1、Excel 开发中与线程相关的若干问题参考这个链接:

https://www.cnblogs.com/yangecnu/p/Some-Thread-Releated-Problems-and-Solutions-in-Excel-Development.html

2、SynchronizationContext的实现参考这个链接(共3篇文章,进入链接后可以找到下一章的链接):

https://www.codeproject.com/Articles/31971/Understanding-SynchronizationContext-Part-I

3、ExcelDNA是什么

Excel-DNA is an independent project to integrate .NET into Excel. With Excel-DNA you can make native (.xll) add-ins for Excel using C#, Visual Basic.NET or F#, providing high-performance user-defined functions (UDFs), custom ribbon interfaces and more. Your entire add-in can be packed into a single .xll file requiring no installation or registration.

https://excel-dna.net/

4、ExcelDNA下实现一个自定义的SynchronizationContext

https://groups.google.com/forum/#!searchin/exceldna/synchronizationcontext%7Csort:date/exceldna/pyybn0lSP8k/3DW6NOekCgAJ

ExcelDNA中的解决方案

 1  dynamic app;
 2         dynamic worksheet;
 3         
 4         public void Test(IRibbonControl ribbonControl)
 5         {
 6             app = ExcelDnaUtil.Application;
 7             worksheet = app.ActiveSheet;
 8             worksheet.Cells[2, 2].Value = Thread.CurrentThread.ManagedThreadId;
 9             Action<object> action = Run;
10             Task task = new Task(action,ExcelSynchronizationContext.Current);
11             task.Start();
12           
13         }
14         private void Run(object context)
15         {
16             worksheet.Cells[1, 2].Value = Thread.CurrentThread.ManagedThreadId;
17 
18             for (int i = 0; i < 50; i++)
19             {
20                 Thread.Sleep(100);
21                 ExcelAsyncUtil.QueueAsMacro(UpdateUI, i);
22             }
23         }
24      
25         private void UpdateUI(object state)
26         {
27             int i = (int)state + 1;
28             worksheet.Cells[i, 1].Value = i;
29             worksheet.Cells[i, 3].Value = Thread.CurrentThread.ManagedThreadId;
30         }
 Task task = new Task(action,ExcelSynchronizationContext.Current);

ExcelSynchronizationContext.Current为主线程的SynchronizationContext。
 ExcelAsyncUtil.QueueAsMacro(UpdateUI, i);

其实是执行了SynchronizationContext的Post方法。

上面是ExcelDNA里集成的SynchronizationContext实现,有兴趣可以→


尝试实现自定义一个SynchronizationContext
一个SendOrPostCallbackItem实例为一组SendOrPostCallback委托与方法、参数
 1 internal enum ExecutionType
 2     {
 3         Post,
 4         Send
 5     }
 6     internal class SendOrPostCallbackItem
 7     {
 8         object state;
 9         ExecutionType executionType;
10         SendOrPostCallback callbackMethod;
11 
12         internal SendOrPostCallbackItem(SendOrPostCallback callback, object state, ExecutionType executionType)
13         {
14             this.callbackMethod = callback;
15             this.state = state;
16             this.executionType = executionType;
17         }
18 
19         internal void Execute()
20         {
21             if (executionType == ExecutionType.Post)
22                 Post();
23             else
24                 Send();
25         }
26         internal void Send()
27         {
28             throw new NotImplementedException();
29         }
30 
31         internal void Post()
32         {
33             callbackMethod(state);
34         }
35     }
DiyExcelSynchronizationContext,在ExcelDNA下实现一个SynchronizationContext
public class DiyExcelSynchronizationContext : SynchronizationContext
    {
        private const string RunMacroName = "ExcelSyncContext.Run";
        private static readonly ConcurrentQueue<SendOrPostCallbackItem> queue = new
    ConcurrentQueue<SendOrPostCallbackItem>();
        private static readonly TimeSpan BackoffTime = TimeSpan.FromSeconds(1);
        [ExcelCommand(Name = RunMacroName)]
        public static void Run()
        {
            while (true)
            {
                SendOrPostCallbackItem workItem;
                if (!queue.TryDequeue(out workItem))
                {
                    return;
                }
                workItem.Execute();
            }
        }
        dynamic application = ExcelDnaUtil.Application;
        public override void Post(SendOrPostCallback d, object state)
        {

            SendOrPostCallbackItem item = new SendOrPostCallbackItem(d, state, ExecutionType.Post);
            queue.Enqueue(item);
            Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    try
                    {
                        application.Run(RunMacroName);
                        break;
                    }
                    catch (COMException e1)
                    {
                        if (IsRetry(e1))
                        {
                            Thread.Sleep(BackoffTime);
                            continue;
                        }
                        return;
                    }
                    catch (Exception e2)
                    {
                        return;
                    }
                }

            });
        }
        public const uint RPC_E_SERVERCALL_RETRYLATER = 0x8001010A;
        public const uint RPC_E_CANTCALLOUT_INASYNCCALL = 0x800AC472;

        public static bool IsRetry(COMException e)
        {
            var errorCode = (uint)e.ErrorCode;
            switch (errorCode)
            {
                case RPC_E_SERVERCALL_RETRYLATER:
                case RPC_E_CANTCALLOUT_INASYNCCALL:
                    return true;
                default:
                    return false;
            }
        }
    }

 

 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!