COM组件的线程模型与Excel多线程的背景知识
COM组件的线程模型被称之为Apartment模型,COM对象初始化时其执行上下文(Execution Context),他要么和单个线程关联STA(Single Thread Apartment ) 要么和多个线程关联MTA(Multi Thread Apartment)。Excel是一种STA线程的应用程序,使用多线程直接怼会出问题,必须借助线程的同步上下文实现线程间的消息传递。
1、Excel 开发中与线程相关的若干问题参考这个链接:
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.
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;
}
}
}
来源:oschina
链接:https://my.oschina.net/u/4258768/blog/3537515