Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/273.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在新线程上使用ObservableCollection_C#_Wpf_Multithreading_Dispatcher - Fatal编程技术网

C# 在新线程上使用ObservableCollection

C# 在新线程上使用ObservableCollection,c#,wpf,multithreading,dispatcher,C#,Wpf,Multithreading,Dispatcher,几天前,由于无法从另一个线程更新ObservableCollection,我创建了一个。 这是线程的解决方案: Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate { TheTVDB theTvdb = new TheTVDB(); foreach (TVSeries tvSeries in theTvdb.SearchSeries("Dexter")) { this.Overview.

几天前,由于无法从另一个线程更新ObservableCollection,我创建了一个。 这是线程的解决方案:

Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate
{
    TheTVDB theTvdb = new TheTVDB();
    foreach (TVSeries tvSeries in theTvdb.SearchSeries("Dexter"))
    {
        this.Overview.Add(tvSeries);
    }
}),
DispatcherPriority.Background);
但是,这似乎不是真正的解决方案,因为在执行委托时UI仍然冻结。我的猜测是,上面的代码实际上并没有在另一个线程上运行任何东西,而是将其全部分派到UI线程。 因此,我真正想做的是自己创建一个新线程并进行加载(这发生在
theTvdb.SearchSeries()
)。然后,我将迭代结果并将其添加到我的
ObservableCollection
中,这必须在UI线程上进行

这种方法听起来正确吗

我想出了下面的方法,我认为可以加载结果并将它们添加到ObervableCollection中,并在我的列表视图中显示它们,而不会冻结UI

Thread thread = new Thread(new ThreadStart(delegate
{
    TheTVDB theTvdb = new TheTVDB();
    List<TVSeries> dexter = theTvdb.SearchSeries("Dexter");

    foreach (TVSeries tvSeries in dexter)
    {
        Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate
        {
            this.Overview.Add(tvSeries);
        }),
        DispatcherPriority.Normal);
    }
}));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

您的方法是危险的,在很短的时间内将大量作业推送到dispatcher上可能会导致应用程序暂停或冻结。当您的一般方法很好时,您可能需要考虑使用批添加元素到列表中。

另外,您不能使用,因为您现在正在使用当前线程的调度程序。因此,您要求您的线程处理同一线程中的添加,而不是ui线程。您需要从ui线程获取调度程序。例如,可以使用应用程序对象


根据我的经验,我还建议您使用它,它在WPF中比普通线程更好。

在应用程序中的任何“繁重”工作中使用多线程的方法,您希望保持响应,这是正确的思考方式,因此您走上了正确的轨道

然而,当您在这里创建和使用其他线程时,仍然过于依赖调度程序。考虑到,在这里使用多线程,您的过程应该如下:

  • 在单独的螺纹上进行重载提升
  • 完成后,请调度员根据需要更新UI
  • 这模拟了调度器上的负载

    你考虑过使用任务吗?从“干净的代码”的角度来看,它们非常好,但在这里是适用的,因为通过任务延续,一旦在UI线程上完成繁重的工作,您可以将任务链接在一起,以调用UI上的相关代码

    请看一看,这是一个良好的开端

    如果你需要的话,我很乐意提供一个更详细的例子

    编辑:正如在另一个答案中提到的,BackgroundWorker在这里同样有效。。。从线程的角度来看,最终结果是完全相同的。我只是喜欢任务语法

    编辑:我只是想提供一些代码。为了简单起见,我现在将避免继续。考虑下面的方法来做你的重举:

        public void HeavyLifting(Action<List<Items>> callback)
        {
            Task<List<Items>> task = Task.Factory.StartNew(
                () =>
                    {
                        var myResults = new List<Items>();
    
                        // do the heavy stuff.
    
                        return myResults;
                    });
    
            callback.Invoke(task.Result);
        }
    
    然后,在任务完成时执行回调时传递方法。请注意,这里是我要求调度员执行工作的地方:

    private void HandleHeavyLiftingCompleted(List<Items> results)
    {
        this._uiDispatcher.BeginInvoke(
            new Action(() => { this.MyItems = new ObservableCollection<Items>(results); }));
    }
    
    private void handlehavylifting已完成(列出结果)
    {
    这个.\u uiDispatcher.BeginInvoke(
    新操作(()=>{this.MyItems=newobserveCollection(results);});
    }
    
    请注意,在本例中,涉及的UI工作是更新我从视图绑定到的ObvservableCollection。在这里的示例中,我使用了一个随机的“Item”对象,它可以是您喜欢的任何对象

    我使用的是Cinch,因此依赖于一个服务来获取相关的Dispatcher(您在这里看到的是。\ uiDispatcher)。在您的情况下,您可以使用此处其他问题中提到的方法获得对它的引用

    另外,如果您有时间阅读,这里有一些关于WPF线程模型的重要信息。

    您可以简单地执行以下操作:

    Task.Factory.StartNew(() => 
    {
        var theTvdb = new TheTVDB();
        var dexterSeries = theTvdb.SearchSeries("Dexter");
        Application.Current.Dispatcher.Invoke(new Action(() => 
        {    
            foreach (var tvSeries in dexterSeries)
            {
                this.Overview.Add(tvSeries);
            }
        }));
    });
    

    您可能会发现,不管怎样,如果可观察集合包含很多项,并且您的UI很复杂,那么UI还是会冻结—我们在这里讨论的是WPF/SL还是Winforms?目前它实际上并不包含很多元素。我不知道有多少,但不超过10个。我使用的是WPF。你的线程代码看起来还不错——你创建了一个新线程并在上面开始了一个非常简单的操作,你能发布你的XAML代码吗?我假设你的数据库在另一个盒子上,或者你正在本地机器上运行所有的东西?我已经更新了我的问题,以显示用于视图的XAML。我通过API从外部加载数据。当使用从
    应用程序.Current.dispatcher
    检索的dispatcher而不是
    dispatcher.Current
    时,我将得到以下错误:
    必须在与DependencyObject相同的线程上创建DependencySource。
    您知道为什么会发生这种情况吗?关于BackgroundWorker,这不是一个选项,无法将其设置为STA,我使用的某些图像转换需要它。出现错误似乎是因为我在另一个线程上创建位图。在位图上调用
    Freeze()
    (例如
    myBitmap.Freeze()
    )解决了错误,现在它似乎可以工作了。这听起来像是在另一个线程上创建了一些东西,应该是ui线程。您是否在线程上创建任何ui元素?哪个线程是集合的所有者?是的,位图必须被冻结才能在创建它们的线程之外的其他线程中使用。我一定会尝试一下这个任务。另一方面,BackgroundWorker似乎不是解决这个问题的方法,因为我无法将BackgroundWorker设置为STA,这是我使用的某些图像转换所必需的。然而,图像转换似乎确实是个问题。我在另一个线程上创建了位图。在这些位图上调用
    Freeze()。我将添加一些代码供参考,以供任务使用。。。祝你好运
    
    Task.Factory.StartNew(() => 
    {
        var theTvdb = new TheTVDB();
        var dexterSeries = theTvdb.SearchSeries("Dexter");
        Application.Current.Dispatcher.Invoke(new Action(() => 
        {    
            foreach (var tvSeries in dexterSeries)
            {
                this.Overview.Add(tvSeries);
            }
        }));
    });