C# 加入线程会毫无例外地停止执行
我的应用程序有两个线程——主UI线程和另一个线程 应用程序在单独的线程上收集文件名列表。然后我希望这个列表显示在我的GUI中。在线程完成之前,程序将按需要工作。它会导致以下错误C# 加入线程会毫无例外地停止执行,c#,multithreading,C#,Multithreading,我的应用程序有两个线程——主UI线程和另一个线程 应用程序在单独的线程上收集文件名列表。然后我希望这个列表显示在我的GUI中。在线程完成之前,程序将按需要工作。它会导致以下错误 "The calling thread must be STA, because many UI components require this." 下面的代码正在运行,正如您所看到的,它包含两个操作参数——一个用于AddCurrentFile,另一个称为Complete。在复制过程中,GetDuplicateList
"The calling thread must be STA, because many UI components require this."
下面的代码正在运行,正如您所看到的,它包含两个操作参数——一个用于AddCurrentFile,另一个称为Complete。在复制过程中,GetDuplicateList()调用这两个方法
this._duplicatesThread = new Thread(() =>
{
try
{
duplication.GetDuplicateList(new Action<string>(AddCurrentFile), new Action<List<Duplicate>>(Complete));
CreateParent();
}
catch (Exception ex)
{
string s = ex.ToString();
throw;
}
});
因此,为了安抚线程,我在CreateParent()方法中添加了this.\u duplicatesThread.Join代码>
然而,我认为(请纠正/同意我的观点)此时发生的事情是因为我加入了线程,它实际上取消了非UI线程,因此在进程的这一点上停止!因此,它从不执行this之后的行代码>
现在,我希望程序员们不要问得更好,但是,我感觉这里的问题更多的是我的设计而不是代码(尽管我很高兴知道模式和代码都是垃圾)
我的问题是,既然我不能通过新线程(因为它不是UI线程)来实现这一点,我需要做什么?这就好像我需要一个委托,比如NewThread.FinishedExecuting+=DoThisThing()
您应该将ViewModel与视图(UI)分离,并使用WPF数据绑定将它们绑定在一起。实际上,可以从后台工作线程更新ViewModel对象,WPF框架将自动为数据绑定UI控件(至少在.NET 4.5中是AFAIK)封送INotifyPropertyChanged.PropertyChanged
通知。这样,控件将根据需要在主UI线程上自动更新自己
但是,我更愿意以与UI元素相同的方式对待ViewModel,即手动将所有ViewModel更新从后台线程封送到主UI线程。这可以通过Dispatcher.Invoke
(同步)或Dispatcher.InvokeAsync
(建议异步)完成
还可以使用Progress
模式将更新传播到UI线程。以下是一个相关的问题/答案:
听起来就像您只需要从另一个线程调用UI元素上的.Invoke()
。您不应该从创建UI组件的线程之外的线程访问UI组件。也就是说,您可以通过使用大多数UI组件都具有的.Invoke()
,来封送对UI线程的调用。这里有一个简单的示例:对不起@sircodesalot,您是对的-我的代码中有一个错误,我已经更新了。异常将在构造函数中引发!因此,在这个阶段,它只是创建一个对象,而不是将对象“分配”给一个控件。也就是说,即使您在一个单独的线程上创建一个组件,该线程运行于单个单元之外(根据WPF的要求),您将永远无法将该组件与UI的其余部分连接起来,因为只要WPF尝试从原始UI线程更新它,它就会由于上述原因而失败。简而言之,您应该使用一个UI线程,然后通过.Invoke()
封送对UI线程的调用。这样想,当创建控件时,它会记录创建它的线程,然后每次访问该控件时,它都会进行验证检查,以确保只有同一线程访问它。这很酷,但现在你有两个问题。如果在此线程上创建控件,如何将其附加到主UI线程上的其他UI?您仍然可以从主UI线程指向此对象,但基本上整个UI需要在sta上运行。在主ui类的上方添加[StatThread]啊,后台工作线程做了所有的事情,就像那样-它甚至还有我希望的完整委托!!
private void Complete(List<Duplicate> duplicateList)
{
this._duplicateList = duplicateList;
}
private void CreateParent()
{
ObservableCollection<DuplicateControl> parent = new ObservableCollection<DuplicateControl>();
foreach (var duplicate in this._duplicateList)
{
DuplicateControl ic = new DuplicateControl();//This fails as soon as I enter the constructor. The DuplicateControl uses the UserControl as a base class
parent.Add(ic);
}
this.ParentDuplicate = parent;
}
Thread thread = new Thread(MethodWhichCallesTheConstructor);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
thread.Join(); //Wait for the thread to end