MainThread blocks when executing a task that interacts with COM

跟風遠走 提交于 2019-12-13 00:07:27

问题


I want to execute some long lasting tasks in a C# gui application (Windows Forms based). I first tried to execute these in parallel with Task.Factory.StartNew().

The tasks involve COM interaction. When I execute the task (which should have been executed in parallel) the main/gui thread shows the busy cursor and does not respond.

The object through which the COM calls are made was created in the main thread which uses the single threaded apartment. In these posts:

  • Blocking method of an STA COM object is a design defect?
  • C# COM Cross Thread

I read that this might be the reason.

So my question is: If this is the reason what would be a remedy?

Here is what the code looks like (simplified):

    // the Main() method is a STA which could be important?
    [STAThread]
    static void Main()
    {
     //...
    }


    // inside the MainForm class
    private AComWrapperCreatedByCSharp comWrapper;

    private void button1Click(object sender, EventArgs e)
    {
        var myCancellationSource = new CancellationTokenSource();

        if (someCondition)
        {

            // both alternatives did not work

            //Task.Factory.StartNew(
            ThreadPool.QueueUserWorkItem(
                _ =>
                {
                    try
                    {
                        CancellationToken cancellationToken = myCancellationSource.Token;

                        // the main thread will respond while this tasks sleeps!
                        Thread.Sleep(5000);

                        cancellationToken.ThrowIfCancellationRequested();

                        // that's where the task's thread is when I pause execution 
                        // while the gui is not responding
                        var result = comWrapper.CallSeveralLongerLastingCOMOperations();

                        cancellationToken.ThrowIfCancellationRequested();

                        this.BeginInvoke(new UpdateDelegate(() =>
                        {
                            handle(result);
                        }));
                    }
                    catch (OperationCanceledException)
                    {
                        return;
                    }
                    catch (Exception ex)
                    {
                        threadSafeLogging(string.Format(@"An exception occurred: ""{0}""", ex));
                        return;
                    }
                }

                // I tried adding these lines when starting the Task.Factory.... solution
                //,"factory started thread",_updateProjectsDevicesAndLibrariesTaskCancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default
                );

        }
    }

Thanks

This is the solution that @Hans Passant mentioned:

    // inside the MainForm class
    // the COM access object is no longer created in the main thread
    private AComWrapperCreatedByCSharp comWrapper; // = new AComWrapperCreatedByCSharp;

    private void someInitializationMethodInTheMainThread()
    {
      var th = new Thread(() =>
        {
          comWrapper = CreateComWrapper();
        });
      th.SetApartmentState(ApartmentState.MTA);
      th.Start();
      th.Join();
    }

If I got it right, the only purpose of this thread is to create an object in the not STA of the main thread!

来源:https://stackoverflow.com/questions/9602783/mainthread-blocks-when-executing-a-task-that-interacts-with-com

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