我有一个返回一个整数数组(当然,在他们自己的专有格式)的专有COM库。 当我从主UI线程访问此阵,一切都很好,跑得快。 当我从另一个线程访问它时,访问速度很慢。 有下面一些示例代码。
private void test() {
ProprietaryLib.Integers ints = ProprietaryLib.GetInts();
int x;
for(int i = 0; i < 500; i++)
for(int j = 0; j < ints.Count; j++)
x = ints[j];
}
private void button1_Click(object sender, EventArgs e) {
test(); // Very little time
new System.Threading.Thread(() => test()).Start(); // Lots of time
}
这究竟是为什么? 有没有什么办法,我加快这? 如果我使用多线程处理,而不是多线程,我会再有获得良好表现的希望? (唉虽然听起来很复杂。)
更新:
我很满意下面的答案,但想在这里补充一些数据供参考(我自己和其他人的)。
创建和如上所示给出了关于每个存取为12ns在一个新的线程访问的对象。 据推测,该对象实际上是在主线程创建的速度慢是因为从那里编组的数据。
如果明确创建主线程,但它的访问中标记为单线程公寓一个新的线程上的数据,访问时间更慢,每访问15纳秒。 我猜.NET必须有一些额外的开销,以保持公寓尼斯,虽然它让我担心的是我不知道的开销是什么。 只需2-3 ns的差异不会有太大虽然。
如果你创建和访问一个新的线程对象标记STA的时间融化每访问.2ns。 但是,这是新的线程真的安全吗? 这是另一个问题,我想到一个问题。
COM对象有线程亲和力,他们可以告诉COM它们不是线程安全的。 在注册表中的一个键,“的ThreadingModel”键。 绝大多数做的,通过指定“公寓”或只是忽略的关键。 它是在.NET不太明确,它使用MSDN告诉你,类不是线程安全的,否则不提醒你,你忘了阅读文章。 绝大多数.NET类的都不是线程安全的,从COM组件类没有什么不同。 与.NET,COM确保它们被调用的线程安全的方式。 通过自动编组调用创建该对象的线程。
换句话说,没有并发速度很慢。
要出人头地的唯一方法是创建自己的线程,并调用其SetApartmentState()方法切换到STA,一个幸福家庭的COM对象不是线程安全的。 你必须创建该线程上COM对象也是如此。 你可能有泵消息循环,以保持它活着,一个STA的要求。 永远不要阻塞线程。 这些是使它成为一个幸福的家一类不是线程安全的,如果所有的呼叫都在一个线程中做那么没有什么可以出错的地方。 你可以找到这样一个线程的样本实现在这个答案 。
或者换句话说,存在使用与不是线程安全对象的线程时,没有免费的午餐。 .NET允许您通过遗忘在需要的地方使用锁 ,COM使得自动拍摄你的脚。 少了很多程序员跳跃单腿这种方式,但效率不高。
这可能取决于线程单元模型。 如果您使用的是单线程单元模型(STA),你可能遭受的性能问题(如果数据尺寸足够大)。 如果你能(然后如果你不使用,需要STA另一个COM对象),你可能会尝试你的公寓模式更改为MTA(多线程单元模型)。
注:的WinForms 不能与MTA兼容,它总是会检查,因为一些COM对象和它使用(剪贴板和拖放等)需要的公寓模型是单线程的。 我从来没有尝试过,但如果你不使用该功能,也许它的工作原理。
从MSDN :
因为对象调用不以任何方式系列化,多线程并发对象提供最高的性能和采用多处理器硬件的最佳优势,为跨线程,跨进程和跨机器调用。
其他参考
这里的SO: 你能解释一下STA和MTA?
MSDN: MTAThreadAttribute
尝试使用UI线程上执行COM调用Invoke()
private void button1_Click(object sender, EventArgs e) {
ThreadPool.QueueUserWorkItem(delegate {
this.Invoke((Action)(() => {
test();
}));
});
}
做你的长期运行的操作中,这家呼叫之前和之后Invoke()
因此,只有快速COM调用在UI线程中运行。 此外,这取决于你在做什么,你也许可以摆脱大量的支架和其他线路噪声的。