- Tak.Run()、asyncにより手軽に非同期コードが書ける様になったとは言え困難で複雑な非同期処理。この記事は「基本的な非同期」の学習プログラムです
- ボタンを押してテキストBOX、リストBOXの表示とコードを見比べてどの様に動作するか確認します
- CUIとGUIでは挙動が違うことがあるのでGUIしか使わない方はGUIで学習して下さい
- ~Asyncという非同期専用メソッドが用意されている場合は特別な事情がある場合を除いてTask.Runではなく専用メソッドを使用します
- 経験上、Dispatcher.Invoke()の入れ忘れ(例外がスローされない事がある)や、waitが掛からないと思ったらSystem.Threading.Thread.Sleep()の所をawait Task.Delay()にしてたり、というのがままありました
・あるサイトによると、
1.戻り値の型は void
、Task
、Task<T>
のみ。
2.非同期シーケンス(awaitとyield returnの混在)に対応していない。
この二点の改善要望がとっくの前に挙がってるものの相当困難なのか未だ実装できない
ようです。
※訂正:出来るように変わった様です。コメント欄ご参照ください。
非同期処理は特に通信、CG関係では必須です。当方の掲載記事、
- スターフィールド
- B型リアルタイムオペレーショングラフ
- 2D CGライブラリSkiaSharp「トライアングル」
- ニコニコ動画風 メッセージ
- 非同期 Wait 試験
- DGRAM(UDP) チャット 等は非同期コード含みますのでご参照ください
1.動作画面
10と13番を押した様子。
正しく123の順番で表示され3つの非同期コードが同期しているのが確認できる。
- 00.カウンタが始動。01.でキャンセル
- 0A.スレッドIDの確認
- 0C.ValueTask構造体? (よくわからず。利用者さんの方で補完・修正して下さい)
- 0F.http受信。プロクシのアカウントは変更
- 10.ManualRestEvant Taskが2つ動く。11で停止、12で再始
- 13.AutoResetEvant 3つの非同期コードを同期的に動作させる。必ず123の順番で動く
- 7番の「非同期の終了状態」は修正しました
非同期関係は他にもあるのでコード修正、ボタン追加する等してご自分用に改変して下さい
2.WPF C# Sourcecode
//////////////////////////////////////////////////////
// WPF .NET FRAMEWORK. 基本的非同期検証 詰め合わせセット
// 一部、他のサイト等から引用したコード含む
// inf102 S.H 2023-2024
//////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WPF_TASK {
public partial class MainWindow : Window {
////////////////////////////////////////////////////////////////////
static CancellationTokenSource tokenSource;
static int v=1;
static ManualResetEvent mr1 = new ManualResetEvent(false);
static AutoResetEvent ar1 = new AutoResetEvent(false);
static AutoResetEvent ar2 = new AutoResetEvent(false);
static AutoResetEvent ar3 = new AutoResetEvent(false);
/////////////////////////////////////////////////////////////////////
public MainWindow() {
InitializeComponent();
}
void LA(string st){
listBox1.Items.Add (st);
var border = VisualTreeHelper.GetChild(listBox1, 0) as Border;
var listBoxScroll = border.Child as ScrollViewer;
listBoxScroll.ScrollToEnd();
}
void T(string t){
textBox1.Text+=t;
textBox1.ScrollToEnd();
}
void RESET(){
listBox1.Items.Clear();
textBox1.Text="";
textBox2.Text="";
}
/////////////////////////////////////////////////////////////////////////////////
// 0. 2TASK RUN.関数外からのCANCEL (CancellationTokenSource)
/////////////////////////////////////////////////////////////////////////////////
private void Button_Click(object sender, RoutedEventArgs e) {
RESET();
tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
Task.Run(() => Proc1(token));
Task.Run(() => Proc2());
LA ("Task.Run() x2 Started");
}
// 非同期1 キャンセル付き
void Proc1(CancellationToken token){
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadA CONTER START");
}));
var i = 0;
while (true){
System.Threading.Thread.Sleep (10);
this.Dispatcher.Invoke((Action)(() =>{
textBox2.Text = (i++).ToString();
}));
if (token.IsCancellationRequested) break;
}
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadA CONTER STOP");
}));
}
// 非同期2
private void Proc2() {
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadB START");
}));
Thread.Sleep(3500);
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadB END");
}));
}
// 1. 非同期をキャンセル
private void Button_Click_2(object sender, RoutedEventArgs e) {
if (tokenSource != null) tokenSource.Cancel();
}
/////////////////////////////////////////////////////////////////////////////////////////
// 2. 2タスク起動 awaitなので待ちとなる await task.run void main(void)
/////////////////////////////////////////////////////////////////////////////////////////
private async void Button_Click_1(object sender, RoutedEventArgs e) {
RESET();
// TASK 書き方1
await Task.Run (proc_a1);
// ここで待ち状態
// TASK 書き方2
await Task.Run ( ()=> {
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadB Start");
}));
System.Threading.Thread.Sleep(2000);
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadB End");
}));
});
// ここで待ち状態
LA ("ALL END");
}
private void proc_a1(){
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadA Start");
}));
System.Threading.Thread.Sleep(4000);
this.Dispatcher.Invoke((Action)(() =>{
LA("ThreadA End");
}));
}
//////////////////////////////////////////////////////////////////////////////////////////
// 3. para fnc (void)
//////////////////////////////////////////////////////////////////////////////////////////
private async void Button_Click_3(object sender, RoutedEventArgs e) {
RESET();
LA ("START");
LA ("WAIT 3Sec");
Task<string> HeavyTask = HeavyAsyncOperation();
// await
string result = await HeavyTask;
LA ("TASKのRET値 ->" + result);
LA ("ALL END");
}
// 重い処理
static async Task<string> HeavyAsyncOperation() {
await Task.Delay(3000);
return "RET is AAA";
}
///////////////////////////////////////////////////////////////////////
// 4. param main (void)
//////////////////////////////////////////////////////////////////////
public async void Button_Click_11(object sender, RoutedEventArgs e) {
RESET();
LA ("START");
textBox1.Text="";
int ret1 = await Task.Run(ProcFunc);
LA ("RET1="+ret1.ToString());
LA ("ALL END");
}
public int ProcFunc() {
//時間のかかる処理
System.Threading.Thread.Sleep(2000);
this.Dispatcher.Invoke((Action)(() =>{
listBox1.Items.Add(v);
}));
return 999;
}
/////////////////////////////////////////////////////////////////////////////////////////
// 5. WPARAM fnc (LPARAM) 3TASK Start / 全TASK終了待ち
/////////////////////////////////////////////////////////////////////////////////////////
private async void Button_Click_5(object sender, RoutedEventArgs e) {
RESET();
textBox1.Text="3TASK start / 全TASK終了待ち ";
////////////////////////////////////////////////////////////////
List<int> value1 = new List<int>(new int[] { 1, 2, 3 });
List<int> value2 = new List<int>(new int[] { 1, 4, 1 });
List<int> value3 = new List<int>(new int[] { 2, 10, 5, 8, 1, 4 });
////////////////////////////////////////////////////////////////
// スレッド起動
Task<int> t1 = Task.Factory.StartNew(() => ProcA(value1));
Task<int> t2 = Task.Factory.StartNew(() => ProcB(value2));
Task<int> t3 = Task.Factory.StartNew(() => ProcC(value3));
////////////////////////////////////////////////////////////////
int[] results = await Task.WhenAll<int> (new Task<int>[] { t1, t2, t3});
textBox1.Text += " ALL TASK END. RET..." + string.Format("t1={0:d}, t2={1:d}, t3={2:d}\r\n", results[0], results[1], results[2]);
LA ("ALL END");
}
private int ProcA(List<int> values) {
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadA START");
}));
Thread.Sleep(500);
int result = 0;
foreach (int v in values) {
result += v;
}
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadA END");
}));
return result;
}
private int ProcB(List<int> values) {
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadB START");
}));
Thread.Sleep(2000);
int result = 0;
foreach (var v in values) {
result += v;
}
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadB END");
}));
return result;
}
private int ProcC(List<int> values) {
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadC START");
}));
Thread.Sleep(3000);
int result = 0;
foreach (var v in values) {
result += v;
}
this.Dispatcher.Invoke((Action)(() =>{
LA ("ThreadC END");
}));
return result;
}
////////////////////////////////////////////////////////////////////////////////////////
// 6. void fnc (para) 全タスクが完了するまで待機
////////////////////////////////////////////////////////////////////////////////////////
private async void Button_Click_4(object sender, RoutedEventArgs e) {
RESET();
LA ("4THREAD 同時START");
var t1 = CalcAsync2("A THREAD");
var t2 = CalcAsync2("B THREAD");
var t3 = CalcAsync2("C THREAD");
var t4 = CalcAsync2("XX");
await Task.WhenAll (t1, t2, t3,t4);
// 待ち状態
LA ("4THREAD ALL END");
}
private async Task CalcAsync2(string a){
await Task.Run(() =>{
this.Dispatcher.Invoke((Action)(() =>{
LA("["+ a +"] START");
}));
if ( a != "XX" ) Thread.Sleep(1500);
if ( a == "XX" ) Thread.Sleep(5000);
});
}
///////////////////////////////////////////////////////////////
// 7. 非同期処理の終了状態を取得する void fnc (LPARAM)
///////////////////////////////////////////////////////////////
private async void Button_Click_7(object sender, RoutedEventArgse) {
RESET();
LA ("2TASK START..");
// Task1の実行
Task task1 = Task.Run(() => {
SomeMethod(5000);
});
await Task.Delay(10);
LA ("1---------------------");
// Task2の実行
Task task2 = Task.Run(() => {
SomeMethod(2500);
});
LA ("2---------------------");
// Task1, Task2 どちらか終わるまで待機
await Task.WhenAny(task1, task2);
LA ("どちらか終了");
bool flg1=false;
bool flg2=false;
while (true){
// task1の完了チェック
if(task1.IsCompleted){
LA ("Task1(Wait 5000)終了");
flg1=true;
}
else{
LA ("Task1(Wait 5000)稼働中");
}
// task2の完了チェック
if(task2.IsCompleted ){
LA ("Task2(Wait 2500)終了");
flg2=true;
}
else{
LA ("Task2(Wait 2500)稼働中");
}
if (flg1 == true && flg2==true ) break;
await Task.Delay(1000);
}
LA ("ALL END");
}
// void fnc(PARAM) どちらかのスレッドの終了待ち
public void SomeMethod(int millisec){
this.Dispatcher.Invoke((Action)(() =>{
LA ("SomeMethod " + millisec + " 開始");
}));
Thread.Sleep(millisec);
this.Dispatcher.Invoke((Action)(() =>{
LA ("SomeMethod " + millisec + " 終了");
}));
}
//////////////////////////////////////////////////////////////////////////////
// 8. void fnc (PARAM) どちらかのスレッドの終了待ち
/////////////////////////////////////////////////////////////////////////////
private async void Button_Click_8(object sender, RoutedEventArgs e) {
RESET();
// Taskの実行
Task task1 = Task.Run(() => {
SomeMethod(6000);
});
// Taskの実行
Task task2 = Task.Run(() => {
SomeMethod(3000);
});
// task1かtask2が終わるまで待機
await Task.WhenAny (task1, task2);
textBox1.Text=("---->どちらか終了. その内全て終了.");
}
///////////////////////////////////////////////////////////////////////
// 9. void fnc (WPARAM,LPARMA)
//////////////////////////////////////////////////////////////////////
private async void Button_Click_9(object sender, RoutedEventArgs e) {
RESET();
string str1 = "PARAM1";
string str2 = "PARAM2";
LA ("P1 START");
await Task.Run(() => proc6(str1,str2));
LA ("P1 END");
LA ("ALL END");
}
private void proc6(object WPARAM,object LPARAM) {
System.Threading.Thread.Sleep(3000);
string parameter_string1 = "";
string parameter_string2 = "";
if (WPARAM != null) {
parameter_string1 = (string)WPARAM;
}
if (LPARAM != null) {
parameter_string2 = (string)LPARAM;
}
this.Dispatcher.Invoke((Action)(() =>{
LA ("proc1 End WPARAM.." + parameter_string1);
LA ("proc1 End LPARAM.." + parameter_string2);
}));
}
///////////////////////////////////////////////////////////////////////////////
// A. TASK別スレッド
///////////////////////////////////////////////////////////////////////////////
private void Button_Click_10(object sender, RoutedEventArgs e) {
RESET();
v=0;
LA ("TASK.RUN START");
Task.Run(() => HeavyMethod5("TASK.RUN 1st"));
LA ("MAIN ThreadID " + Thread.CurrentThread.ManagedThreadId.ToString());
LA ("TASK.RUN END");
}
// 別スレッド
private void HeavyMethod5(string str){
string pid= (Thread.CurrentThread.ManagedThreadId.ToString());
Thread.Sleep(2500);
this.Dispatcher.Invoke((Action)(() =>{
LA ("CHILD 1st ThreadID " + pid);
LA (str);
}));
// 重い処理をした続きの処理
SomethingNextMethod("2nd");
}
// こっちも別スレッドになる
void SomethingNextMethod(string str){
string pid= (Thread.CurrentThread.ManagedThreadId.ToString());
this.Dispatcher.Invoke((Action)(() =>{
LA ("CHILD 2nd ThreadID " +pid);
LA ("NEXTMETHOD " + str);
}));
Thread.Sleep(1500);
this.Dispatcher.Invoke((Action)(() =>{
LA ("NEXTMETHOD / ALL END");
}));
}
////////////////////////////////////////////////////////////////////////////////////////
// B. LPARAM fnc (WPARAM) タスク内で中止
////////////////////////////////////////////////////////////////////////////////////////
private async void Button_Click_6(object sender, RoutedEventArgs e) {
RESET();
string myTask = await GenerateTextAsync("START");
LA ( myTask);
LA ("ALL END");
}
private async Task<string> GenerateTextAsync(string xx){
int c=0;
this.Dispatcher.Invoke((Action)(() =>{
LA (xx);
}));
return await Task.Run <string>( () => {
while(true){ // ここを変数にして ループ抜ける方法もある
if( c>10 ){
// return "Canceled";
break;
}
this.Dispatcher.Invoke((Action)(() =>{
LA ( c++.ToString() );
}));
Thread.Sleep( 200 );
}
return "return data";
});
}
///////////////////////////////////////////////////////////////////////
// C. ValueTask構造体? 非同期が必要な場合にのみ内部でTaskのインスタンスが作られるらしい
//////////////////////////////////////////////////////////////////////
private async void Button_Click_12(object sender, RoutedEventArgs e) {
RESET();
LA ("START");
string ret1 = await Task.Run(() => Hoge5(100));
LA (ret1);
string ret2= await Task.Run(() => Hoge5(10));
LA (ret2);
LA ("ALL END");
}
async Task<string> Hoge5(int num){
if(num == 100){
await Task.Delay(3000);
return "OK";
}
return "NG";
}
//////////////////////////////////////////////////////////////////////////////
// D. void fnc (para複数)
//////////////////////////////////////////////////////////////////////////////
private async void Button_Click_13(object sender, RoutedEventArgs e) {
RESET();
LA("START");
object[] LPARAM = { "DATA", 4.2 };
await Task.Run(() => proc61(LPARAM));
LA ("ALLEND");
}
public async void proc61(object LPARAM){
// objectの配列型に変換
this.Dispatcher.Invoke((Action)(() =>{
textBox1.Text= ("RCV PARAM1->" + ((object[])LPARAM)[0].ToString() + " / PARAM2->" + (double)((object[])LPARAM)[1]);
}));
System.Threading.Thread.Sleep(2000);
//await Task.Delay(5000);
}
//////////////////////////////////////////////////////////////////////////////
// E. WPARAM fnc (LPRAM1,LPRAM2)
//////////////////////////////////////////////////////////////////////////////
private async void Button_Click_15(object sender, RoutedEventArgs e) {
RESET();
string str1 = "PARAM1";
string str2 = "PARAM2";
LA ("P1 START");
int ret=await Task.Run(() => proc7(str1,str2));
LA (ret.ToString());
LA ("P1 END/ALL END");
}
private int proc7 (object WPARAM,object LPARAM) {
System.Threading.Thread.Sleep(2000);
string parameter_string1 = "";
string parameter_string2 = "";
if (WPARAM != null) {
parameter_string1 = (string)WPARAM;
}
if (LPARAM != null) {
parameter_string2 = (string)LPARAM;
}
this.Dispatcher.Invoke((Action)(() =>{
LA ("proc1 End WPARAM.." + parameter_string1);
LA ("proc1 End LPARAM.." + parameter_string2);
}));
return 999;
}
/////////////////////////////////////////////////////////////
// F. WWW HTTPデータ取得
/////////////////////////////////////////////////////////////
private async void Button_Click_14(object sender, RoutedEventArgs e) {
RESET();
// プロクシ使用
HttpClientHandler ch = new HttpClientHandler();
ch.Proxy = new WebProxy("http://webproxy.xxxxxxx.ac.jp:8080");
ch.Proxy.Credentials = new NetworkCredential("xx", "xx"); // usr,pw
ch.UseProxy = true;
HttpClient client = new HttpClient(ch);
await Task.Run(async () =>{
try{
var task = client.GetStringAsync("https://www.yahoo.co.jp/");
await task;
this.Dispatcher.Invoke((Action)(() =>{
textBox1.Text = task.Result;
}));
}
catch (Exception ex){
this.Dispatcher.Invoke((Action)(() =>{
textBox1.Text = ex.GetBaseException().ToString();
}));
}
});
}
///////////////////////////////////////////////////////////////////////////////
// UIブロッキング確認用プログレスバ
///////////////////////////////////////////////////////////////////////////////
private void Window_Initialized(object sender, EventArgs e) {
P1.Maximum= 100;
P1.Value=0;
bool FLG=false;
_= Task.Run(() =>{
while (true){
this.Dispatcher.Invoke((Action)(() =>{
if (FLG ==false) P1.Value++;
else P1.Value--;
if (FLG ==false && P1.Value>99) FLG=true;
if (FLG == true && P1.Value<=0) FLG=false;
}));
Thread.Sleep(30);
}
});
}
///////////////////////////////////////////////////////////////////////////////
// ManualResetEvent [1] 複数Task.Run の停止と再始
///////////////////////////////////////////////////////////////////////////////
private void Button_Click_34(object sender, RoutedEventArgs e) {
textBox1.Text="";
mr1.Set();
LA ("Singan ON ");
// TASK1
_=Task.Run ( () => {
while(true) {
// シグナルON 待ち
mr1.WaitOne();
this.Dispatcher.Invoke((Action)(() =>{
T("A ");
}));
Thread.Sleep(250);
}
});
// TASK2
_=Task.Run ( () => {
while(true) {
// シグナルON 待ち
mr1.WaitOne();
this.Dispatcher.Invoke((Action)( () =>{
T("B ");
}));
Thread.Sleep(250);
}
});
LA("Task.Run() x2 Started");
}
// ManualResetEvent[2] シグナルON (開始)
private void Button_Click_39(object sender, RoutedEventArgs e) {
mr1.Set();
LA ("Singan ON ");
}
// ManualResetEvent[3] シグナルOFF (停止)
private void Button_Click_40(object sender, RoutedEventArgs e) {
mr1.Reset();
LA ("Signal OFF ");
}
///////////////////////////////////////////////////////////////////////////////
// AutoResetEvent[1] 同期的に非同期スレッド3つを制御 1,2,3の順番
///////////////////////////////////////////////////////////////////////////////
private void Button_Click_20(object sender, RoutedEventArgs e) {
ar1.Set();
LA("Task.Run() x3 Started");
// TASK1
_=Task.Run ( () => {
while(true) {
ar1.WaitOne();
this.Dispatcher.Invoke((Action)( () =>{
T("1 ");
}));
ar2.Set();
Thread.Sleep(1000);
}
});
// TASK2
_=Task.Run ( () => {
while(true) {
ar2.WaitOne();
this.Dispatcher.Invoke((Action)( () =>{
T("2 ");
}));
ar3.Set();
Thread.Sleep(1000);
}
});
// TASK3
_=Task.Run ( () => {
while(true) {
ar3.WaitOne();
this.Dispatcher.Invoke((Action)( () =>{
T("3 ");
}));
ar1.Set();
Thread.Sleep(2000);
}
});
}
// AutoResetEvent [2] 停止
private void Button_Click_44(object sender, RoutedEventArgs e) {
LA ("全停止");
ar3.Reset();
}
// AutoResetEvent [3] 再開
private void Button_Click_49(object sender, RoutedEventArgs e) {
LA ("同期的に非同期再開");
ar1.Set();
}
///////////////////////////////////////////////////////////////////////////////
}
}
3.XAML Sourcecode
<!-- ----------------- -->
<!-- TASKTEST -->
<!-- inf102 2023 -->
<!-- ----------------- -->
<Window x:Class="WPF_TASK.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF_TASK"
mc:Ignorable="d" Title="非同期 検証詰め合わせセット WPF版" Height="550" Width="1000" Background="#FF7E9EEC" Initialized="Window_Initialized">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="3"/>
<RowDefinition Height="52"/>
<RowDefinition Height="52"/>
<RowDefinition Height="52"/>
<RowDefinition Height="52"/>
<RowDefinition Height="52"/>
<RowDefinition Height="52"/>
<RowDefinition Height="52"/>
<RowDefinition Height="52"/>
<RowDefinition Height="70*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="230"/>
<ColumnDefinition Width="220*"/>
<ColumnDefinition Width="220*"/>
<ColumnDefinition Width="220*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="listBox1" Grid.Row="1" Margin="3,1,2,2" FontSize="13" Grid.RowSpan="9" Background="#FFD2E3FF" />
<TextBox x:Name="textBox1" FontSize="15" Grid.RowSpan="1" VerticalScrollBarVisibility="Visible" AcceptsReturn="True" Margin="1,1,2,2" Grid.Row="9" TextWrapping="Wrap" Grid.ColumnSpan="4" Grid.Column="1" Background="White"/>
<Button Grid.Column="1" Grid.Row="1" FontSize="17" Content="0. COUNTER START" VerticalAlignment="Center" Height="50" Margin="1,0,1,0" Click="Button_Click" Foreground="RED"/>
<Button Grid.Column="2" Grid.Row="1" FontSize="17" Content=" 1. COUNTER STOP
(CancellationTokenSource)" VerticalAlignment="Center" Height="50" Margin="1,0,1,0" Click="Button_Click_2" Foreground="RED"/>
<Button Grid.Column="1" FontSize="17" Content=" 2. 2タスク起動
 ait void fnc (void)" VerticalAlignment="Center" Height="50" Margin="1,0,1,0" Grid.Row="2" Click="Button_Click_1"/>
<Button Grid.Column="2" FontSize="17" Content="3. para fnc (void) [1]" VerticalAlignment="Center" Height="50" Margin="1,0,1,0" Grid.Row="2" Click="Button_Click_3"/>
<Button Grid.Column="2" FontSize="17" Content="6. 4TASK 全終了待ち
 void fnc (para)" VerticalAlignment="Center" Height="50" Grid.Row="3" Click="Button_Click_4" Margin="1,0,1,0" />
<Button Grid.Column="1" FontSize="17" Content="5. 3TASK 全終了待ち
 para1 fnc (para2)" VerticalAlignment="Center" Height="50" Margin="1,0,1,0" Grid.Row="3" Click="Button_Click_5"/>
<Button Grid.Column="1" FontSize="17" Content="B. Task.Runの中で中止
 para1 fnc (para2)" VerticalAlignment="Center" Height="50" Margin="1,0,1,0" Grid.Row="5" Click="Button_Click_6" />
<Button Grid.Column="3" FontSize="17" Content="7. 非同期の終了状態を取得
 void fnc (para)" VerticalAlignment="Center" Height="50" Margin="1,0,2,0" Grid.Row="3" Click="Button_Click_7" />
<Button Grid.Column="1" FontSize="17" Content="8. 2TASK start どちらかのタスク
終了待ち void fnc (para)" VerticalAlignment="Center" Height="50" Margin="1,0,1,0" Grid.Row="4" Click="Button_Click_8"/>
<Button Grid.Column="2" FontSize="17" Content="9. void fnc (para1,para2)" VerticalAlignment="Center" Height="50" Margin="1,0,1,0" Grid.Row="4" Click="Button_Click_9" />
<Button Grid.Column="3" FontSize="17" Content="4. para fnc (void) [2]" VerticalAlignment="Center" Height="50" Margin="1,0,2,0" Grid.Row="2" Click="Button_Click_11"/>
<Button Grid.Column="2" FontSize="17" Content="C. ValueTask構造体" VerticalAlignment="Center" Height="50" Margin="1,0,1,0" Grid.Row="5" Click="Button_Click_12" />
<TextBox Grid.Row="1" x:Name="textBox2" TextAlignment="Center" Grid.Column="3" Foreground="RED" FontSize="30" VerticalAlignment="Center" Height="45" Margin="12,2,12,0" BorderBrush="#FFFB124C"/>
<Button Grid.Column="3" FontSize="18" Content="D. void fnc (para複数)" VerticalAlignment="Center" Margin="1,0,2,0" Grid.Row="5" Height="50" Click="Button_Click_13" />
<Button Grid.Column="1" FontSize="18" Content="E. para1 fnc (para2,para3)" Grid.Row="6" VerticalAlignment="Center" Height="50" Click="Button_Click_15" Margin="1,0,1,0"/>
<StackPanel Grid.Column="3" Grid.Row="6" Margin="3,1,3,0">
<TextBlock Text="UIブロッキング確認用" Margin="12,4,12,0" Width="101" />
<ProgressBar x:Name="P1" Height="24" Margin="12,2,12,22" Maximum="100"/>
</StackPanel>
<Button Grid.Row="6" Grid.Column="2" Margin="1,0,1,0" FontSize="18" Content="F. HTTP受信" Height="50" VerticalAlignment="Center" Click="Button_Click_14" />
<Button Grid.Column="3" Content=" A.別スレッド 
 ThreadID CHK." Grid.Row="4" Margin="1,0,2,0" FontSize="18" Height="50" VerticalAlignment="Center" Click="Button_Click_10"/>
<Button Grid.Row="7" Grid.Column="1" Margin="1,0,1,0" FontSize="18" Content="10. ManualResetEvent" Height="50" VerticalAlignment="Center" Click="Button_Click_34" Foreground="Green"/>
<Button Grid.Row="7" Grid.Column="3" Margin="1,0,1,0" FontSize="18" Content="12. ManualResetEvent
 Singnal ON" Height="50" VerticalAlignment="Center" Click="Button_Click_39" Foreground="Green"/>
<Button Grid.Row="7" Grid.Column="2" Margin="1,0,1,0" FontSize="18" Content="11. ManualResetEvent
 Signal OFF" Height="50" VerticalAlignment="Center" Click="Button_Click_40" Foreground="Green"/>
<Button Grid.Row="8" Grid.Column="1" Margin="1,0,1,0" FontSize="18" Content="13. AutoResetEvent" Height="50" VerticalAlignment="Top" Click="Button_Click_20" Foreground="BLUE" />
<Button Grid.Row="8" Grid.Column="2" Margin="1,0,1,0" FontSize="18" Content="14. AutoResetEvent
 停止" Height="50" VerticalAlignment="Center" Click="Button_Click_44" Foreground="BLUE" />
<Button Grid.Row="8" Grid.Column="3" Margin="1,0,1,0" FontSize="18" Content="15. AutoResetEvent
 再始" Height="50" VerticalAlignment="Top" Click="Button_Click_49" Foreground="BLUE" Grid.RowSpan="2" />
</Grid>
</Grid>
</Window>