4

I'm new in TPL world, and I did that code:

    var myItems = myWpfDataGrid.SelectedItems;

    this.Dispatcher.BeginInvoke(new Action(() =>
    {
        var scheduler = new LimitedConcurrencyLevelTaskScheduler(5);
        TaskFactory factory = new TaskFactory(scheduler);

        foreach (MyItem item in myItems)
        {
            Task myTask = factory.StartNew(() =>

            DoLoooongWork(item)                   

                ).ContinueWith((t) =>
                {
                    Debug.WriteLine(t.Exception.Message);
                    if (t.Exception.InnerException != null)
                    {
                        Debug.WriteLine(t.Exception.InnerException.Message);
                    }
                },
                TaskContinuationOptions.OnlyOnFaulted);
        }
    }), null);            

The only one access to gui is "var myItems = myWpfDataGrid.SelectedItems;" and it is read only! The function "DoLoooongWork()" does access to serial ports, etc. It's a separated SDK function that doesn't access the GUI. I know that "Dispatcher.BeginInvoke" is a bit redundant, but I don't know what I can do, or what I'm doing wrong. The only reason to this code is to free the GUI while "DoLoooongWork()" executes, but the GUI is frozen!

What's wrong with that code?

edit

Thanks to @Euphoric help, I discovered the problem that is similar to that post: COM Interop hang freezes entire COM system. How to cancel COM call

Community
  • 1
  • 1
Click Ok
  • 8,700
  • 18
  • 70
  • 106
  • 1
    Did you try doing it without the factory, eg. Just new Task and Start? And without the dispatcher. Something like http://blog.yojimbocorp.com/2012/05/22/using-task-for-responsive-ui-in-wpf/ – Euphoric Jan 29 '14 at 18:03
  • @Euphoric Yes, I did. In truth, my original code is without the factory and without the dispatcher. I added in my despair :) – Click Ok Jan 29 '14 at 18:23
  • 1
    Does the freeze happen when you replace DoLoooongWork with Thread.Sleep? – Euphoric Jan 29 '14 at 19:10
  • @Euphoric Man, you are a genius! I replace with Thread.Sleep(60), and the GUI is free! I'm happy and sad... This is a direct call to an Interop DLL, that talks with serial ports and etc... How is it possible freeze the GUI? – Click Ok Jan 29 '14 at 20:07
  • @Euphoric Oh, please, promote your comment to answer and I can mark as correct :) I know that my problems are not resolved, but you answered correctly the question! – Click Ok Jan 29 '14 at 20:08
  • 1
    @ClickOk, maybe if you show what's exactly going on inside `DoLoooongWork`, we might be able to help further. Here's a somewhat similar problem, FYI: http://stackoverflow.com/q/21211998/1768303. And yes, `Dispatcher.BeginInvoke` is redundant here. – noseratio Jan 29 '14 at 20:20
  • @Noseratio: It's zkemkeeper SDK. The code is only "zkemkeeper.CZKEM axCZKEM1 = new zkemkeeper.CZKEM();" and "axCZKEM1.Connect_Net(ip, port);" I read your link and replaced my LimitedConcurrencyLevelTaskScheduler with ThreadAffinityTaskScheduler and it didn't works... sorry my "noobism" here; I'm a mere TPL user and you know a lot of core concepts :) – Click Ok Jan 29 '14 at 20:44
  • @ClickOk, check my updated answer with more thoughts about zkemkeeper SDK. – noseratio Jan 30 '14 at 07:17

2 Answers2

3

I presume some objects inside DoLoooongWork require thread affinity and message pumping. Try my ThreadWithAffinityContext and see if helps, use it like this:

private async void Button_Click(object sender, EventArgs e)
{
    try 
    {           
        using (var staThread = new Noseratio.ThreadAffinity.ThreadWithAffinityContext(
             staThread: true, pumpMessages: true))
        {
            foreach (MyItem item in myItems)
            {
                await staThread.Run(() =>
                {
                    DoLoooongWork(item);
                }, CancellationToken.None);
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

More info about ThreadWithAffinityContext.

[UPDATE] You mentioned in the comments that the code inside DoLoooongWork looks like this:

zkemkeeper.CZKEM axCZKEM1 = new zkemkeeper.CZKEM(); 
axCZKEM1.Connect_Net(ip, port);

I never heard of "zkemkeeper" before, but I did a brief search and found this question. Apparently, Connect_Net only establishes the connection and starts a session, while the whole communication logic happens asynchronously via some events, as that question suggests:

bIsConnected = axCZKEM1.Connect_Net("192.168.0.77", Convert.ToInt32("4370"));
if (bIsConnected == true)
{
    iMachineNumber = 1;
    if (axCZKEM1.RegEvent(iMachineNumber, 65535))
    {
        this.axCZKEM1.OnFinger += new kemkeeper._IZKEMEvents_OnFingerEventHandler(axCZKEM1_OnFinger);
        this.axCZKEM1.OnVerify += new zkemkeeper._IZKEMEvents_OnVerifyEventHandler(axCZKEM1_OnVerify);
        // ...
    }
}

That would be a whole different story. Leave a comment if that's the case and you're still interested in some solution.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
1

I had a hunch that something working with serial port would try to use application's event loop to do it's work. So it actually bypasses the whole dispatcher and thread system and blocks the application. I'm not experienced in this field so I don't know how to solve it, but this is different question.

Euphoric
  • 12,645
  • 1
  • 30
  • 44