C#DelegateサブスレッドからUIスレッドのメインスレッドインタフェースを変更する方法
6869 ワード
InvokeとBeginInvokeの理解
一、なぜControlクラスはInvokeとBeginInvokeのメカニズムを提供したのか.
この問題の最も主要な原因はすでにdotnetプログラマーがよく知られているので、私はここでペンを費やして再び自分のログに記録して、後で自分に注意するようにします.
1、windowsプログラムメッセージメカニズム
Windows GUIプログラムはメッセージメカニズムに基づいており、メッセージポンプを維持するメインスレッドがあります.このメッセージポンプはwindowsプログラムを生き生きとさせた.
Windows GUIプログラムのメッセージループ
Windowsプログラムにはメッセージキューがあり、フォーム上のすべてのメッセージがこのキュー内のメッセージの最も主要なソースです.ここでwhileループはGetMessage()という方法を用いているが,これはブロック方法であり,すなわちキューが空の場合に方法がブロックされ,それによってこのwhileループが動きを停止し,一つのプログラムがcpuを理由もなく消費することを回避し,他のプログラムが応答しにくい.もちろん、いくつかのcpuを最大限に動かす必要があるプログラムでは、他の方法を使用することができます.例えば、いくつかの3 dゲームやタイムリーな戦略ゲームでは、一般的にPeekMessage()という方法が使用され、windowsにブロックされず、ゲーム全体のスムーズさとより高いフレームレートを保証します.
このプライマリ・スレッドは、フォーム全体と上のサブコントロールを維持します.メッセージが得られると、DispatchMessageメソッドを呼び出してメッセージを送信します.これにより、フォーム上のウィンドウ・プロシージャの呼び出しが発生します.ウィンドウプロシージャには、もちろんプログラマが提供するフォームデータ更新コードや他のコードがあります.
2、dotnet内のメッセージループ
3、スレッド外操作GUIコントロールの問題
別のスレッドからwindowsフォームのコントロールを操作すると、メインスレッドと競合し、予期せぬ結果、デッドロックになります.そのためwindows GUIプログラミングには、コントロールのスレッドを作成することでコントロールのデータを操作するしかないというルールがあります.そうしないと、予想できない結果が発生する可能性があります.
したがってdotnetでは,これらの問題を容易に解決するために,ControlクラスはISynchronizeInvokeインタフェースを実現し,InvokeとBeginInvokeメソッドを提供し,他のスレッドにGUIインタフェースコントロールを更新させるメカニズムを提供する.
Windowsフォームコントロールをスレッド外から操作する場合は、InvokeメソッドまたはBeginInvokeメソッドを使用して、コントロールが属するスレッドに呼び出しをカプセル化して実行する必要があります.
4、Invoke and BeginInvoke実はメッセージMessageキューを挿入する
Invoke or BeginInvoke
InvokeメソッドまたはBeginInvokeメソッドでは、パラメータとして委任オブジェクトが必要です.依頼はコールバック関数に類似したアドレスであるため,呼び出し者はこの2つの方法で呼び出す必要がある関数アドレスをインタフェーススレッドにカプセル化することができる.これらのメソッドには,コントロール状態を変更するコードが含まれている場合,最終的にこのメソッドを実行するのはインタフェーススレッドであるため,競合条件を回避し,予想できない問題を回避する.他のスレッドがインタフェーススレッドが属するコントロールを直接操作すると、競合条件が発生し、予想できない結果になります.
Invokeを使用して委任メソッドのカプセル化を完了することは,SendMessageメソッドを使用してインタフェーススレッドにメッセージを送信するのと同様の同期メソッドである.すなわち,Invoke封入の方法が実行されるまでInvokeメソッドは返されず,呼び出しスレッドがブロックされる.
BeginInvokeメソッドを使用して、PostMessageを使用して通信するのと同様に、非同期メソッドを送信します.すなわち,このメソッドのカプセル化が完了するとすぐに戻り,依頼メソッドの実行が完了するのを待つことなく,呼び出し者スレッドがブロックされることはない.しかし、呼び出し元は、EndInvokeメソッドまたはWaitHandleのような他のメカニズムを使用して、非同期動作の完了を待つこともできる.
しかし、内部実装では、InvokeとBeginInvokeはいずれもPostMessage法を用いており、SendMessageによる問題を回避している.一方,Invoke法の同期ブロッキングはWaitHandle機構によって達成された.
3、使用場面の問題
バックグラウンドスレッドがUIコントロールのステータスを更新した後、待つ必要がなく、次の処理を続ける場合は、BeginInvokeを使用して非同期処理を行う必要があります.
バックグラウンドスレッドがUIコントロールを操作する必要があり、操作が実行されるまで待つ必要がある場合は、Invokeを使用します.そうでなければ、バックグラウンドスレッドとプライマリ断面スレッドが何らかの状態データを共有している場合、同期呼び出しではなく、それぞれ実行を継続すると、デッドロックは発生しないものの、予期せぬ表示結果やデータ処理エラーが発生する実行シーケンス上の問題が発生する可能性があります.
ISynchronizeInvokeには、InvokeRequiredという属性が表示されます.このプロパティは、1つのオブジェクトがUIコントロールにアクセスするときにInvokeまたはBeginInvokeを使用して封止する必要があるかどうかをプログラミング時に決定するために使用されます.必要でなければ直接更新できます.このプロパティは、呼び出し元オブジェクトとUIオブジェクトが同じスレッドに属している場合にfalseを返します.後述のコード解析では,Controlクラスがこの属性を実装することは,呼び出し者とコントロールが同じスレッドに属しているかどうかを判断することであることが分かる.
三、Delegate.BeginInvoke
同期方法の非同期呼び出しは、1つの依頼によって行うもよい.Netが提供する非同期呼び出しメカニズムの1つです.でもねBeginInvoke法は,ThreadPoolからスレッドを取り出してこの方法を実行し,非同期実行効果を得た.すなわち,このようにして複数の非同期依頼をコミットすると,これらの呼び出しの順序が保証されない.また,スレッドプール内のスレッドを用いてタスクを完了するため,頻繁に使用するとシステムの性能に影響を及ぼす.
Delegate.BeginInvokeはまた、非同期メカニズムによって方法を実行するために、他のスレッドに委任方法をカプセル化することを意味する.呼び出し者スレッドは、封止が完了した後、その作業を継続することができます.しかし,この方法で封入された最終実行スレッドは,実行ライブラリがThreadPoolから選択したスレッドである.
ここでは、Controlクラスの非同期呼び出しBeginInvokeが新しいスレッドを開いて依頼タスクを完了するのではなく、インタフェースコントロールの属するスレッドに依頼タスクを完了させるという誤りを修正する必要があります.非同期操作が新しいスレッドを開くという説は必ずしも正確ではないようだ.
Test実戦:
一、なぜControlクラスはInvokeとBeginInvokeのメカニズムを提供したのか.
この問題の最も主要な原因はすでにdotnetプログラマーがよく知られているので、私はここでペンを費やして再び自分のログに記録して、後で自分に注意するようにします.
1、windowsプログラムメッセージメカニズム
Windows GUIプログラムはメッセージメカニズムに基づいており、メッセージポンプを維持するメインスレッドがあります.このメッセージポンプはwindowsプログラムを生き生きとさせた.
Windows GUIプログラムのメッセージループ
Windowsプログラムにはメッセージキューがあり、フォーム上のすべてのメッセージがこのキュー内のメッセージの最も主要なソースです.ここでwhileループはGetMessage()という方法を用いているが,これはブロック方法であり,すなわちキューが空の場合に方法がブロックされ,それによってこのwhileループが動きを停止し,一つのプログラムがcpuを理由もなく消費することを回避し,他のプログラムが応答しにくい.もちろん、いくつかのcpuを最大限に動かす必要があるプログラムでは、他の方法を使用することができます.例えば、いくつかの3 dゲームやタイムリーな戦略ゲームでは、一般的にPeekMessage()という方法が使用され、windowsにブロックされず、ゲーム全体のスムーズさとより高いフレームレートを保証します.
このプライマリ・スレッドは、フォーム全体と上のサブコントロールを維持します.メッセージが得られると、DispatchMessageメソッドを呼び出してメッセージを送信します.これにより、フォーム上のウィンドウ・プロシージャの呼び出しが発生します.ウィンドウプロシージャには、もちろんプログラマが提供するフォームデータ更新コードや他のコードがあります.
2、dotnet内のメッセージループ
public static void Main(string[] args)
{
Form f = new Form();
Application.Run(f);
}
Dotnet while , Application.Run 。
3、スレッド外操作GUIコントロールの問題
別のスレッドからwindowsフォームのコントロールを操作すると、メインスレッドと競合し、予期せぬ結果、デッドロックになります.そのためwindows GUIプログラミングには、コントロールのスレッドを作成することでコントロールのデータを操作するしかないというルールがあります.そうしないと、予想できない結果が発生する可能性があります.
したがってdotnetでは,これらの問題を容易に解決するために,ControlクラスはISynchronizeInvokeインタフェースを実現し,InvokeとBeginInvokeメソッドを提供し,他のスレッドにGUIインタフェースコントロールを更新させるメカニズムを提供する.
public interface ISynchronizeInvoke
{
[HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)]
IAsyncResult BeginInvoke(Delegate method, object[] args);
object EndInvoke(IAsyncResult result);
object Invoke(Delegate method, object[] args);
bool InvokeRequired { get; }
}
}
Windowsフォームコントロールをスレッド外から操作する場合は、InvokeメソッドまたはBeginInvokeメソッドを使用して、コントロールが属するスレッドに呼び出しをカプセル化して実行する必要があります.
4、Invoke and BeginInvoke実はメッセージMessageキューを挿入する
Invoke or BeginInvoke
InvokeメソッドまたはBeginInvokeメソッドでは、パラメータとして委任オブジェクトが必要です.依頼はコールバック関数に類似したアドレスであるため,呼び出し者はこの2つの方法で呼び出す必要がある関数アドレスをインタフェーススレッドにカプセル化することができる.これらのメソッドには,コントロール状態を変更するコードが含まれている場合,最終的にこのメソッドを実行するのはインタフェーススレッドであるため,競合条件を回避し,予想できない問題を回避する.他のスレッドがインタフェーススレッドが属するコントロールを直接操作すると、競合条件が発生し、予想できない結果になります.
Invokeを使用して委任メソッドのカプセル化を完了することは,SendMessageメソッドを使用してインタフェーススレッドにメッセージを送信するのと同様の同期メソッドである.すなわち,Invoke封入の方法が実行されるまでInvokeメソッドは返されず,呼び出しスレッドがブロックされる.
BeginInvokeメソッドを使用して、PostMessageを使用して通信するのと同様に、非同期メソッドを送信します.すなわち,このメソッドのカプセル化が完了するとすぐに戻り,依頼メソッドの実行が完了するのを待つことなく,呼び出し者スレッドがブロックされることはない.しかし、呼び出し元は、EndInvokeメソッドまたはWaitHandleのような他のメカニズムを使用して、非同期動作の完了を待つこともできる.
しかし、内部実装では、InvokeとBeginInvokeはいずれもPostMessage法を用いており、SendMessageによる問題を回避している.一方,Invoke法の同期ブロッキングはWaitHandle機構によって達成された.
3、使用場面の問題
バックグラウンドスレッドがUIコントロールのステータスを更新した後、待つ必要がなく、次の処理を続ける場合は、BeginInvokeを使用して非同期処理を行う必要があります.
バックグラウンドスレッドがUIコントロールを操作する必要があり、操作が実行されるまで待つ必要がある場合は、Invokeを使用します.そうでなければ、バックグラウンドスレッドとプライマリ断面スレッドが何らかの状態データを共有している場合、同期呼び出しではなく、それぞれ実行を継続すると、デッドロックは発生しないものの、予期せぬ表示結果やデータ処理エラーが発生する実行シーケンス上の問題が発生する可能性があります.
ISynchronizeInvokeには、InvokeRequiredという属性が表示されます.このプロパティは、1つのオブジェクトがUIコントロールにアクセスするときにInvokeまたはBeginInvokeを使用して封止する必要があるかどうかをプログラミング時に決定するために使用されます.必要でなければ直接更新できます.このプロパティは、呼び出し元オブジェクトとUIオブジェクトが同じスレッドに属している場合にfalseを返します.後述のコード解析では,Controlクラスがこの属性を実装することは,呼び出し者とコントロールが同じスレッドに属しているかどうかを判断することであることが分かる.
三、Delegate.BeginInvoke
同期方法の非同期呼び出しは、1つの依頼によって行うもよい.Netが提供する非同期呼び出しメカニズムの1つです.でもねBeginInvoke法は,ThreadPoolからスレッドを取り出してこの方法を実行し,非同期実行効果を得た.すなわち,このようにして複数の非同期依頼をコミットすると,これらの呼び出しの順序が保証されない.また,スレッドプール内のスレッドを用いてタスクを完了するため,頻繁に使用するとシステムの性能に影響を及ぼす.
Delegate.BeginInvokeはまた、非同期メカニズムによって方法を実行するために、他のスレッドに委任方法をカプセル化することを意味する.呼び出し者スレッドは、封止が完了した後、その作業を継続することができます.しかし,この方法で封入された最終実行スレッドは,実行ライブラリがThreadPoolから選択したスレッドである.
ここでは、Controlクラスの非同期呼び出しBeginInvokeが新しいスレッドを開いて依頼タスクを完了するのではなく、インタフェースコントロールの属するスレッドに依頼タスクを完了させるという誤りを修正する必要があります.非同期操作が新しいスレッドを開くという説は必ずしも正確ではないようだ.
Test実戦:
changeBtnText c;
public Form1()
{
InitializeComponent();
GxIAPINET.IGXFactory.GetInstance().Init();
refreshcamer_Click();
// ,
SetStyle(
ControlStyles.OptimizedDoubleBuffer
| ControlStyles.ResizeRedraw
| ControlStyles.Selectable
| ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint
| ControlStyles.SupportsTransparentBackColor,
true);
c = new changeBtnText(flashTextChange);
}
public void flashTextChange(String text)
{
this.pupil_flash_btn.Text = text;
}
//---- ----
public void onFramecallback(object obj, IFrameData objIFrameData)
{
lock (obj1) {
f1++;
m_objCFps1.IncreaseFrameNum();
//foreach (Point p in gbm1.pointlist) {
// Console.WriteLine(p.X);
//}
//if (f1 % 10 == 0)
//{
//gbm1.Myfuseshow(objIFrameData);
double radius = 0;
Point p = new Point();
byte[] leftpupil = gbm1.getByteImg(objIFrameData, out p, out radius, f1, calculatewhat);
//gbm1.Show(objIFrameData);
// ped.PdLeftImage.Add(leftpupil);
ped.PdLeftPointData.Add(p);
ped.PdLeftRadius.Add(radius);
if (calculatewhat == 2)
{
int flashnumleft = ped.PdLeftRadius.Count;
//Console.WriteLine(flashnumleft);
if (flashnumleft == 1500)
{
suc_num_global_flash_num++;
}
if (suc_num_global_flash_num == 2)
{
sw.Stop();
Console.WriteLine(" 1500 " + ", :" + sw.ElapsedMilliseconds.ToString());
//this.pupil_flash_btn.Text = " ";
this.BeginInvoke(c," ");
calculatewhat = 0;
}
}
//if (calculatewhat == 1 || calculatewhat == 2)
// this.lefteyepicturebox.Image = CreateBitmap(leftpupil, gbm1.m_nWidth, gbm1.m_nHeigh);
//byte[] leftpupil = gbm1.m_byMonoBuffer;
//Console.WriteLine("--X:"+p.X+"--Y:"+p.Y+"--R:"+radius);
//leftCameraImage = gbm1.SaveBmp(objIFrameData);
//}
}
}