【问题标题】:Parallel ForEach with Thread and Wait completion具有线程和等待完成的并行 ForEach
【发布时间】:2020-11-21 09:20:32
【问题描述】:

有一定的队列,需要通过一定的步骤,并创建一个线程(每一步)。反过来,该线程将运行一个与 COM 一起工作的函数(需要一个单独的线程,因为 Internet 上写着 COM 不会受到太大伤害)。

// Для Excel асинхронность очень вредна, т.к. можно поймать COM исключения 0x800A03EC, 0x800AC472 и подобные
// т.к. Excel использует главный поток и при закрытии COM дочерние потоки могут потерять COM интерфейсы
parallelOpt.MaxDegreeOfParallelism = 1;

// Для COM нужен отдельный, личный поток, поэтому многопоточность делаем так
Parallel.ForEach(listsFileInfoExcel, parallelOpt, (listsBlock) =>
{
    try
    {
        Thread t = new Thread(OfficeClass.ExcelProcessing);
        // Данным id будем связывать текущий поток и COM процесс
        string idThread = Guid.NewGuid().ToString();
        lock (objWorkThreadExcelLock)
        {
            dictThreadExcel.Add(idThread, 0);
        }
        try
        {
            t.IsBackground = false;
            t.Start(new Dictionary<string, object> {
                                                            {"listBlock",listsBlock},
                                                            {"pathHeaderImage",ImageHeaderExcel},
                                                            {"pathBackgroundImage",ImageBackgroundExcel},
                                                            {"Action",actionForDocument},
                                                            {"idThread",idThread}
                                                });
            // Скорее всего внешний COM процесс завис
            if (!t.Join(180000))
            {
                throw new Exception($"Принудительно завершён поток с файлами {String.Join(", ", listsBlock.Select(obj => obj.FullName).ToArray())}");
            }
        }
        catch (Exception ex)
        {
            AddLog(TypeLog.Error, ex.Message);
            t.Abort();
            lock (objWorkThreadExcelLock)
            {
                if (dictThreadExcel[idThread] != 0)
                {
                    Killing(dictThreadExcel[idThread]);
                }
            }
        }
        finally
        {
            lock (objWorkThreadExcelLock)
            {
                dictThreadExcel.Remove(idThread);
            }
        }
    }
    catch { }
});

只有在 Excel 处理中才会出现有趣的功能。也许这与 COM 架构有关? https://stackoverflow.com/a/12893711/4143902(顺便说一下,因为发现这个错误,我把它降低到1个线程)

如果我在具有快速文件系统的快速计算机上运行此代码,则任务管理器 EXCEL.exe 中会出现 1-2 个进程,但如果在具有经典磁盘系统的弱 VM 上运行,这些进程可能有大约35 EXCEL.EXE。问题,我错过了什么?我如何要求异步循环不要进行新的迭代而不确保里面的线程已经结束/死亡??? 页。 s。对于 Word,如果指定 4 个线程,则在 Manager 中保留 4-5 个进程。

【问题讨论】:

  • 你到底在做什么?你是在阻塞线程池中的线程,为每个人创建另一个线程,为每个人做一些废话,然后加入就可以了?使用 object 值创建字典,因为.. 什么,结构过于结构化?
  • Parallel.ForEach的原因是什么?您没有在循环内执行任何 CPU 密集型工作。您只是在启动一个线程,然后等待它完成。另外,如果代码中的 cmets 对于理解问题至关重要,您能否将它们翻译成英文?
  • COM 需要 STAThread 模型(Thread 提供),我使用 Parallel.ForEach 是因为它方便排队。不,cmets 并不是特别有用。问题更多的是为什么 (1 thread Parallel.ForEach) -> (1 thread Thread ?!) -> (1 COM interface ???) 在工作过程中,10分钟后在后台等待几十个EXCEL.EXE来自 COM 接口的进程 ...
  • Excel 不需要STAThread 环境,但需要单线程来创建和交互。并尝试asynchronously。不要用Thread 包装代码,它根本没用。您可以运行一些 Excel,例如最多 4 个实例并使用它打开/关闭工作簿队列。将生产者/消费者编程模式视为一种实现方法。
  • 感谢您的建议,但此链接的想法略有不同 - 打开了 1 个文档并且异步处理了表格。就我而言,只需要 1 个文件、1 个处理程序,并将其设为并行队列。使用额外的内部线程是因为在处理数千个文件时会发生不可预知的情况,并且线程可以无限期地运行。我决定,如果超过 3 分钟,就说明出了问题,你需要关闭紧急状态,即使有工作。

标签: c# multithreading com


【解决方案1】:

我解决了这个问题。问题是由于不注意或缺乏对 COM 类型的理解...

对于历史,也许有人会需要: 叠加检查,发现 EXCEL 生成异常0x800A03EC,后来得到:

Не удалось получить фабрику класса COM для компонента с CLSID {00024500-0000-0000-C000-000000000046} из-за следующей ошибки: 80080005 Ошибка при выполнении приложения-сервера (Исключение из HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE))

翻译大概是这样的:

Retrieving the COM class factory for component with CLSID {00024500-0000-0000-C000-000000000046} failed due to the following error: 80080005 Server execution failed (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE))

这一切是如何发生的:

  1. 外部处理器监控队列,创建嵌套线程
  2. 嵌套线程启动创建 COM 接口的函数
  3. 在某些情况下,它会错误地重置值并使graphic.Filename = "" 而不是 正确 graphic.Filename = null
  4. COM 处理程序 "" 将此作为文件系统的现有路径,什么也没找到,生成 COM 异常
  5. 由于try/catch中的COM处理,异常被捕获,内部线程函数“正确”完成,不需要处理if (!t. Join(180000))并杀死进程
  6. 由于CLR在COM中看到异常,我就跳过去catch,任务管理器中死掉的COM接口和进程堆积EXCEL.EXE

p。 s。 COM 是一个可怕的恶魔......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-09
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 2014-03-18
    • 2018-06-21
    相关资源
    最近更新 更多