Excel多线程 与 SynchronizationContext的实现

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

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

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

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

Excel是一种STA线程的应用程序,使用多线程直接怼会出问题,必须借助线程的同步上下文Send Or Post 消息。

在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;
            }
        }
    }
 

猜你喜欢

转载自www.cnblogs.com/yzhyingcool/p/10865079.html