Question

I have an Excel workbook object containing one sheet, that I want to copy its content into a List.

I have this method:

private Task GeneratePagesList()
{                        
    _pages = new List<Model.Page>();                                                    
    short idCount = 0;

    var generatePagesListTask = new Task(() =>
        {                                                 
            _pages.Add(new Model.Page()
            {
                Url = new Uri(_worksheetRange.Cells[i, j].Value2.ToString(), 
                UriKind.RelativeOrAbsolute),
                Id = idCount
            });
        });
    return generatePagesListTask;
}

Now I want to consume this method and the Task that it returns as follows:

public async void ConvertExelDataAsync()
{                       
    var generatePagesListTask = GeneratePagesList();
    generatePagesListTask.Start();
    await generatePagesListTask;
}

When I run, The operation takes too long, and it never quits the ConvertExelDataAsync method, after a little while (that apparently is 60 sec), I receive an Exception that says:

Managed Debugging Assistant 'ContextSwitchDeadlock' has detected a problem in 'C:\Users\Aymen\Documents\Visual Studio 2013\Projects\WebGraphMaker\WebGraphMaker\bin\Debug\WebGraphMaker.vshost.exe'.

Additional information: The CLR has been unable to transition from COM context 0xd33a5e78 to COM context 0xd33a5fa0 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

Notice: This is the first time I interact with Com objects.

Update 1 :

The Excel consumption works just fine when it's not in a task, once in a task, and the task starts, the problem occurs !

Update 2 : when debugging, once the debugger reaches the line

int rowCount = _worksheetRange.Rows.Count;

It exits and nothing happen, can't explain it.

Update 3 : After opening the Debug>Windows>Threads, it shows this :

enter image description here

The Convert method calls all what's above, defined like this :

public static async void Convert()
        {

            var excelDataConverter = new ExcelDataConverter(ExcelDataReader.ReadData());
            excelDataConverter.ConvertExelDataAsync();
        }

enter image description here

Was it helpful?

Solution

To add to @StepehCleary's answer, the message itself is quite informative:

To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

You have a COM proxy to an out-of-proc Excel COM object, the proxy was created on your main thread (probably an STA UI thread). Then you're accessing it on a worker pool thread (which is an MTA thread).

While the COM proxy object itself may be thread-safe for calls from a worker thread like this, under the hood it most likely trying to marshal the call back to the main thread where the proxy was originally created on. That's where the deadlock occurs.

To stay on the safe side, I suggest you create a dedicated STA thread which does pump messages, create all your COM objects on that threads and call out there.

I have two helper classes for this, ThreadAffinityTaskScheduler and ThreadWithAffinityContext, available here, they should work in any execution environment.

OTHER TIPS

When you're working with async and await, there are some general best practices. One is returning "hot" (running) tasks, so do not use new Task or call Task.Start; use Task.Run instead. Another is to avoid async void; use async Task instead.

However, the core problem is as @HansPassant pointed out: you are creating an STA COM object (the Excel stuff) and then accessing it from a thread pool thread after blocking the STA thread. This is doomed to fail.

Instead, just remove all the async and Task code, and create your list on the STA thread.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top