Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/312.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# 从另一个线程访问MainWindow控件的最佳方法?_C#_Multithreading - Fatal编程技术网

C# 从另一个线程访问MainWindow控件的最佳方法?

C# 从另一个线程访问MainWindow控件的最佳方法?,c#,multithreading,C#,Multithreading,我正在构建一个工作得很好的同步工具,但是一个噪音问题是我有一个很重的方法来冻结UI 现在,我知道我可以用线程或任务来解决这种情况,但是在Sync()方法我有很多访问UI控件的权限 实际上,为了访问主窗口控件,我创建了如下内容: public static MainWindow AppWindow; public MainWindow() { InitializeComponent(); AppWindow = this; } private async void ButtonCl

我正在构建一个工作得很好的同步工具,但是一个噪音问题是我有一个很重的方法来冻结UI
现在,我知道我可以用线程或任务来解决这种情况,但是在
Sync()方法我有很多访问UI控件的权限

实际上,为了访问主窗口控件,我创建了如下内容:

public static MainWindow AppWindow;

public MainWindow()
{
   InitializeComponent();
   AppWindow = this;
}
private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await LongRunningOperation();
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
 private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await Task.Run(() => LongRunningOperation());
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
因此,从任何类中,我都可以简单地执行以下操作:
MainWindow.AppWindow.UIControlName
。你们可以想象,若我使用任务或线程,这个解决方案将变得毫无用处因为我需要为每个控件编写以下内容:

MainWindow.AppWindow.UIControlName.Dispatcher.BeginInvoke(
      (Action)(() => { 
           MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
      }));
假设我有20行或更多这样的代码,代码会变得非常难看和长,以便轻松访问UI控件

现在我想知道异步调用方法并立即返回给调用方而不阻塞UI的最佳方法是什么


如果没有针对此类应用程序的异步编程,用户体验就无法真正实现

您可以使用dispatcher将所有丑陋的东西包装在单独的方法中并使用这些方法,或者像这样使用async\Wait:

public static MainWindow AppWindow;

public MainWindow()
{
   InitializeComponent();
   AppWindow = this;
}
private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await LongRunningOperation();
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
 private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await Task.Run(() => LongRunningOperation());
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
其中LongRunningOperation是:

public async Task<SomeResult> LongRunningOperation() {
    // do some stuff here
    return new SomeResult();
}
您也可以这样做:

public static MainWindow AppWindow;

public MainWindow()
{
   InitializeComponent();
   AppWindow = this;
}
private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await LongRunningOperation();
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
 private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await Task.Run(() => LongRunningOperation());
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
这是因为默认情况下,在等待之前捕获当前同步上下文,而在等待之后恢复此上下文。在UI应用程序(如WPF或WinForms)中,这意味着如果在等待之前您在UI线程上,那么在等待之后您也会在那里,因此可以自由访问UI控件

您可能会问-什么时候应该将方法声明为异步,什么时候只使用
Task.Run
。如果您进行纯CPU计算,则可以使用
Task.Run
。如果您执行任何IO(文件访问、网络访问等)-将方法声明为异步,并使用IO相关类(如
FileStream.ReadAsync
等)提供的异步方法

例如:

private async Task LongRunningOperation() {
    string contents;
    using (var fs = File.OpenRead("some file")) {
        using (var reader = new StreamReader(fs)) {
            contents = await reader.ReadToEndAsync();
        }
    }
    var downloadedFile = await new WebClient().DownloadDataTaskAsync("some file url");
}

您可以使用dispatcher将所有丑陋的东西包装在单独的方法中并使用这些方法,或者像这样使用async\Wait:

public static MainWindow AppWindow;

public MainWindow()
{
   InitializeComponent();
   AppWindow = this;
}
private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await LongRunningOperation();
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
 private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await Task.Run(() => LongRunningOperation());
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
其中LongRunningOperation是:

public async Task<SomeResult> LongRunningOperation() {
    // do some stuff here
    return new SomeResult();
}
您也可以这样做:

public static MainWindow AppWindow;

public MainWindow()
{
   InitializeComponent();
   AppWindow = this;
}
private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await LongRunningOperation();
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
 private async void ButtonClicked(object sender, RoutedEventArgs e) {
    var result = await Task.Run(() => LongRunningOperation());
    MainWindow.AppWindow.UIControlName.Content = "Synchronization in progress";
    await YetAnotherLongRunningOperation();
    // update again
}
这是因为默认情况下,在等待之前捕获当前同步上下文,而在等待之后恢复此上下文。在UI应用程序(如WPF或WinForms)中,这意味着如果在等待之前您在UI线程上,那么在等待之后您也会在那里,因此可以自由访问UI控件

您可能会问-什么时候应该将方法声明为异步,什么时候只使用
Task.Run
。如果您进行纯CPU计算,则可以使用
Task.Run
。如果您执行任何IO(文件访问、网络访问等)-将方法声明为异步,并使用IO相关类(如
FileStream.ReadAsync
等)提供的异步方法

例如:

private async Task LongRunningOperation() {
    string contents;
    using (var fs = File.OpenRead("some file")) {
        using (var reader = new StreamReader(fs)) {
            contents = await reader.ReadToEndAsync();
        }
    }
    var downloadedFile = await new WebClient().DownloadDataTaskAsync("some file url");
}


有很多选择。例如,将SetContent方法添加到您的窗口类中,在其中执行这些丑陋的操作,然后从所有其他地方调用SetContent(“blah”)。或者使用async\await-在异步操作完成后,它会将您返回到UI线程,在那里您可以像往常一样访问窗口属性。@Evk with
async/await
我可以正常访问UI线程控件,而无需创建
操作委托
?您的WPF做错了。使用绑定。@Aron您的提示无法解决此问题。你误解了这个问题。@Dillinger如果你认为WPF绑定与这个问题无关,那么你就误解了WPF绑定。使用真正的MVVM,您可以从后台线程更新VM,绑定类将调用返回到UI线程。你做错了WPF。有很多选择。例如,将SetContent方法添加到您的窗口类中,在其中执行这些丑陋的操作,然后从所有其他地方调用SetContent(“blah”)。或者使用async\await-在异步操作完成后,它会将您返回到UI线程,在那里您可以像往常一样访问窗口属性。@Evk with
async/await
我可以正常访问UI线程控件,而无需创建
操作委托
?您的WPF做错了。使用绑定。@Aron您的提示无法解决此问题。你误解了这个问题。@Dillinger如果你认为WPF绑定与这个问题无关,那么你就误解了WPF绑定。使用真正的MVVM,您可以从后台线程更新VM,绑定类将调用返回到UI线程。你做的WPF是错误的。有趣的解决方案,但我有一个问题。尤其是编译器下划线
await Sync()并显示我:
你不能等待void
,这是同步的声明:
公共void Sync()
,我也在网上读到这不是一个好的实践,声明类型为
void
的方法并使用
异步/等待
,你能确认吗?一般来说,创建异步void函数确实不好。然而,在某些情况下,您别无选择,事件处理程序就是其中之一。您不能更改按钮单击事件处理程序的签名。实际上,这允许void方法异步,只是为了让它们与事件处理程序和类似情况一起工作。请注意,按钮处理程序只是一个示例,您可能会以不同的方式进行操作。是的,明白了。无论如何,我也遇到了同样的问题,
不能等待void
,也许这可以更好地解释:@Dillinger我已经扩展了我的答案。如果你想使用async\await-你必须了解一下它的真正含义。在这种情况下,我应该使用
async/await
所以,我需要访问internetInteresting解决方案,但我有一个问题。尤其是编译器下划线
await Sync()并显示我:
你不能等待void
,这是同步的声明:
公共void Sync()
,我也在网上读到这不是一个好的实践,声明类型为
void
的方法并使用
异步/等待
,你能确认吗?一般来说,创建异步void函数确实不好。然而,在某些情况下