UI スレッド モデルの背景情報
通常、アプリケーションには 1 つの「メイン」UI スレッドがあり、ユーザー (または .NET ランタイム/フレームワーク) がバックグラウンド作業を行う 0 個以上のバックグラウンド/ワーカー/非 UI スレッドがある場合があります。
(...WPF にはレンダリング スレッドと呼ばれる別の特別なスレッドがありますが、今はスキップします...)
たとえば、単純な WPF アプリケーションには、次のスレッドのリストが含まれる場合があります。
また、単純な WinForms アプリケーションには、次のスレッドのリストがある場合があります。

要素を作成すると、その要素は特定の & スレッドに関連付けられ (アフィニティを持ち)、 にDispatcher
関連付けられたスレッドからのみ安全にアクセスできますDispatcher
。
別のスレッドからオブジェクトのプロパティまたはメソッドにアクセスしようとすると、通常、WPF などで例外が発生します。

Windows フォームの場合:

UI への変更は、UI 要素が作成されたのと同じスレッドで実行する必要があります...そのため、バックグラウンド スレッドを使用Invoke/BeginInvoke
して、UI スレッドでその作業を実行します。
非 UI スレッドでの要素作成に関する問題を示すデモ
<Window x:Class="WpfApplication9.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<StackPanel x:Name="mystackpanel">
</StackPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;
namespace WpfApplication9
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Thread m_thread1;
Thread m_thread2;
Thread m_thread3;
Thread m_thread4;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
CreateAndAddElementInDifferentWays();
}
void CreateAndAddElementInDifferentWays()
{
string text = "created in ui thread, added in ui thread [Main STA]";
System.Diagnostics.Debug.WriteLine(text);
CreateAndAddTextChild(text);
// Do NOT use any Joins with any of these threads, otherwise you will get a
// deadlock on any "Invoke" call you do.
// To better observe and focus on the behaviour when creating and
// adding an element from differently configured threads, I suggest
// you pick "one" of these and do a recompile/run.
ParameterizedThreadStart paramthreadstart1 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnThread);
m_thread1 = new Thread(paramthreadstart1);
m_thread1.SetApartmentState(ApartmentState.STA);
m_thread1.Start("[STA]");
//ParameterizedThreadStart paramthreadstart2 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnUIThread);
//m_thread2 = new Thread(paramthreadstart2);
//m_thread2.SetApartmentState(ApartmentState.STA);
//m_thread2.Start("[STA]");
//ParameterizedThreadStart paramthreadstart3 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnThread);
//m_thread3 = new Thread(paramthreadstart3);
//m_thread3.SetApartmentState(ApartmentState.MTA);
//m_thread3.Start("[MTA]");
//ParameterizedThreadStart paramthreadstart4 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnUIThread);
//m_thread4 = new Thread(paramthreadstart4);
//m_thread4.SetApartmentState(ApartmentState.MTA);
//m_thread4.Start("[MTA]");
}
//----------------------------------------------------------------------
void WorkCreatedOnThreadAddedOnThread(object parameter)
{
string threadingmodel = parameter as string;
string text = "created in worker thread, added in background thread, " + threadingmodel;
System.Diagnostics.Debug.WriteLine(text);
CreateAndAddTextChild(text);
}
void WorkCreatedOnThreadAddedOnUIThread(object parameter)
{
string threadingmodel = parameter as string;
string text = "created in worker thread, added in ui thread via invoke" + threadingmodel;
System.Diagnostics.Debug.WriteLine(text);
TextBlock tb = CreateTextBlock(text);
if (tb != null)
{
// You can alternatively use .Invoke if you like!
DispatcherOperation dispop = Dispatcher.BeginInvoke(new Action(() =>
{
// Get this work done on the main UI thread.
AddTextBlock(tb);
}));
if (dispop.Status != DispatcherOperationStatus.Completed)
{
dispop.Wait();
}
}
}
//----------------------------------------------------------------------
public TextBlock CreateTextBlock(string text)
{
System.Diagnostics.Debug.WriteLine("[CreateTextBlock]");
try
{
TextBlock tb = new TextBlock();
tb.Text = text;
return tb;
}
catch (InvalidOperationException ex)
{
// will always exception, using this to highlight issue.
System.Diagnostics.Debug.WriteLine(ex.Message);
}
return null;
}
public void AddTextBlock(TextBlock tb)
{
System.Diagnostics.Debug.WriteLine("[AddTextBlock]");
try
{
mystackpanel.Children.Add(tb);
}
catch (InvalidOperationException ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
public void CreateAndAddTextChild(string text)
{
TextBlock tb = CreateTextBlock(text);
if (tb != null)
AddTextBlock(tb);
}
}
}
セカンダリ UI スレッド、別名「別のスレッドでのトップレベル ウィンドウの作成」
Dispatcher
スレッドが STA アパートメント モデルを使用していることを示す限り、セカンダリ UI スレッドを作成することができますDispatcher.Current
。そのスレッド。Dispatcher.Run()
Dispatcher
ただし、ある UI スレッドで作成された要素を、別の UI スレッドで作成された別の要素の論理/ビジュアル ツリーに配置することはできません。
異なる UI スレッドで作成された要素を混在させるための回避策
制限付きの回避策があります。これにより、ある UI スレッドで作成された要素のレンダリングを、別のスレッドで作成されたビジュアル ツリーで構成する機能が提供される場合がありますHostVisual
。次の例を参照してください。