为什么我的GUI界面会冻结?

4

我是TPL世界的新手,我写了这段代码:

    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);            

唯一可以访问GUI的是“var myItems = myWpfDataGrid.SelectedItems;”,而且它是只读的!函数“DoLoooongWork()”访问串口等,它是一个独立的SDK函数,不访问GUI。我知道“Dispatcher.BeginInvoke”有点多余,但我不知道我能做什么,或者我做错了什么。这段代码的唯一原因是在“DoLoooongWork()”执行时释放GUI,但GUI被冻结了!

这段代码有什么问题?

编辑

感谢@Euphoric的帮助,我发现了类似于这篇文章的问题: COM Interop hang freezes entire COM system. How to cancel COM call


1
你尝试过不使用工厂模式吗?例如只是新建一个任务并启动它?而且不使用调度程序。可以参考这个链接:http://blog.yojimbocorp.com/2012/05/22/using-task-for-responsive-ui-in-wpf/ - Euphoric
1
当您将DoLoooongWork替换为Thread.Sleep时,是否会出现冻结? - Euphoric
@Euphoric Man,你是个天才!我用Thread.Sleep(60)替换了它,GUI变得很流畅!我既高兴又难过......这是对Interop DLL的直接调用,它与串口等通信......怎么可能冻结GUI呢? - Click Ok
@Euphoric 哦,请将您的评论提升为答案,我可以标记为正确 :) 我知道我的问题还没有解决,但您正确回答了这个问题! - Click Ok
1
@ClickOk,如果您展示一下DoLoooongWork内部的具体情况,我们或许可以提供更进一步的帮助。这里有一个类似的问题,供您参考:https://dev59.com/7Oo6XIcBkEYKwwoYKRDG。是的,在这里使用`Dispatcher.BeginInvoke`是多余的。 - noseratio - open to work
显示剩余3条评论
2个回答

3

我想 DoLoooongWork 内的一些对象需要线程亲和性和消息泵。尝试使用我的ThreadWithAffinityContext 类,看看是否有帮助,使用方法如下:

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);
    }
}

有关ThreadWithAffinityContext的更多信息。

[更新]您在评论中提到DoLoooongWork内部的代码看起来像这样:

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

我之前从未听说过"zkemkeeper",但我进行了简短的搜索并发现了这个问题。显然,“Connect_Net”只建立连接并开始会话,而整个通信逻辑通过一些事件异步发生,正如该问题所述:

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);
        // ...
    }
}

如果是这种情况,那就是完全不同的故事了。如果您仍然对某个解决方案感兴趣,请在评论中留言。

1

我有一个预感,与串口通信有关的操作会尝试使用应用程序的事件循环来完成工作。因此,它实际上绕过了整个调度程序和线程系统,并阻塞了应用程序。我在这个领域没有经验,所以不知道如何解决,但这是另一个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接