- PR -

WindowsアプリでUIが固まるのを回避したい

1
投稿者投稿内容
やんたん
ベテラン
会議室デビュー日: 2003/08/18
投稿数: 63
投稿日時: 2003-09-27 22:20
現在C#でWindowsアプリケーションを開発しております。

Webサービスで通信を行っている間もUIを固まらせないという処理を実装したいのですが
その方法がわからず困っています。
いろいろ調べてみた結果、処理の終了をハンドリングしたいので非同期処理を実装しようと思い、下記のようなサンプルを作成しました。
このロジックだと、ウィンドウ自身を移動などはできるのですが、ウィンドウ内のボタンやテキストボックスにはアクセスできません。
また、処理が動き始めて数秒間が完全に画面が固まってしまいます。
この処理のどこかおかしい箇所がありますでしょうか。
また、どのように処理を実装すればよいかご存知でしたら教えて頂けないでしょうか。

よろしくお願いいたします。


private void button1_Click(object sender, System.EventArgs e)
{
Fact fact = new Fact();
Asyncfunc asyncfunc = new Asyncfunc(fact.test);
IAsyncResult asyncResult = asyncfunc.BeginInvoke(100 , null , null);
while(asyncResult.IsCompleted == false)
{
Thread.Sleep(10);
}
long i = asyncfunc.EndInvoke(asyncResult);
textBox1.Text = i.ToString();
}

public delegate long Asyncfunc(long i);

public class Fact
{
public long test(long i)
{
for(int j = 0 ; j < 10000000 ; j++)fact(i);
return fact(i);
}
private long fact(long i)
{
if(i == 1)return 1;
else return i*fact(i-1);
}
}
よねKEN
ぬし
会議室デビュー日: 2003/08/23
投稿数: 472
投稿日時: 2003-09-27 23:51
引用:

やんたんさんの書き込み (2003-09-27 22:20) より:
現在C#でWindowsアプリケーションを開発しております。

Webサービスで通信を行っている間もUIを固まらせないという処理を実装したいのですが
その方法がわからず困っています。



Webサービスのメソッドを呼ぶときにすぐに自分に制御を戻したい場合は
下記の2つの方法が考えられます。

1.OneWayプロパティによる一方向呼び出し
2.非同期呼び出し

Webメソッドを実行するだけで結果を知る必要がなければ1.を使えます。
これは属性でSoapDocumentMethod属性のOneWayプロパティをTrueに設定するだけです。

終了したことを知る必要があれば2.の方法を使うことになります。
Webサービスに定義したWebメソッドに対応する非同期用メソッドが
プロキシクラスに自動的に作成されるので、
WebメソッドがAという名前なら、BeginAというメソッドが同時に用意されています。

このBeginAメソッドの引数でAsyncCallBackを渡すようになっているので、
これによってWebメソッドの処理終了時に呼び出されるメソッドを登録しておき、
そのメソッド内でEndAを呼び出せばメソッドの戻り値も取得できます。

9月頭にたまたま行ったセミナーでごく簡単なWebサービスの作成方法を
デモありで教えてもらったので、その聞いた内容で書いてます。
まだ実践はしてません。なので、間違い等あればご指摘お願いします。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2003-09-28 03:06
引用:

やんたんさんの書き込み (2003-09-27 22:20) より:

また、処理が動き始めて数秒間が完全に画面が固まってしまいます。
この処理のどこかおかしい箇所がありますでしょうか。
また、どのように処理を実装すればよいかご存知でしたら教えて頂けないでしょうか。


何というか、どうやる事で画面が固まらないようになるか、どうやって固まらないようにしようとしているか、ご自身で理解できているでしょうか?

UIが固まらないようにするということは、制御をメッセージループに返してやる必要があるわけで、そのためにはイベントハンドラから帰っている必要がありますよね?
# DoEvents という手もあるにはありますが、制御が混乱しそうなので止めた方が良いように思われます…

引用:

while(asyncResult.IsCompleted == false)
{
Thread.Sleep(10);
}


UIでの操作におけるイベントを処理するスレッドが、非同期メソッドの終了までここでずっと回りつづけているわけですから、UIでの操作を処理する事が出来ないですよね?

これだと、ただ非同期呼び出しを使ってみているだけで、(UIの処理という観点からは)実質同期呼び出しと変わりません。

通常は、イベントハンドラ内でコールバックを指定して非同期呼び出しを行い、すぐにリターンするようにします。
これでリターン直後から、またUIに関するイベント処理が行えるようになります。
非同期呼び出しが完了すると、自動的に指定のメソッドがコールバックされますので、そちらで結果を受け取るようにします。

当然、メソッド実行中にさらにボタンをクリックすると多重で呼び出しがかかることになりますので、その辺は問題なく動くように、例えばボタンが押せないように無効にするなどの対処が必要になるのが普通です。
やんたん
ベテラン
会議室デビュー日: 2003/08/18
投稿数: 63
投稿日時: 2003-09-28 16:11
よねKENさん、なちゃさんアドバイスありがとうございました。
下記サイトを参考にWebサービスの非同期メソッドを用いることにより
実装することができました。
ありがとうございました。

http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpguide/html/cpconinvokingwebservicesasynchronously.asp
make
会議室デビュー日: 2003/10/02
投稿数: 4
投稿日時: 2003-10-02 11:13
Sleepメソッドについてですが、.NET Framework開発者ガイドのなかで以下のような記述を見つけました。

これを見る限り、Sleepメソッド中はUIにアクセスできそうな気がするのですが・・・。
ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1041/cpguide/html/cpovrasynchronousprogrammingoverview.htm

非同期呼び出し完了のポーリング
BeginInvoke によって返された IAsyncResult の IsCompleted プロパティを使用して、非同期呼び出しが完了したことを検出できます。この方法は、ユーザー インターフェイスにサービスを提供するスレッドから非同期呼び出しを行う場合に使用します。完了をポーリングする場合、ユーザー インターフェイス スレッドではユーザー入力の処理を継続できます。

[C#]
public class AsyncMain {
static void Main(string[] args) {
// The asynchronous method puts the thread id here.
int threadId;

// Create an instance of the test class.
AsyncDemo ad = new AsyncDemo();

// Create the delegate.
AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);

// Initiate the asychronous call.
IAsyncResult ar = dlgt.BeginInvoke(3000,
out threadId, null, null);

// Poll while simulating work.
while(ar.IsCompleted == false) {
Thread.Sleep(10);
}

// Call EndInvoke to retrieve the results.
string ret = dlgt.EndInvoke(out threadId, ar);

Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadId, ret);
}
}
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2003-10-06 17:46
引用:

これを見る限り、Sleepメソッド中はUIにアクセスできそうな気がするのですが・・・。
ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1041/cpguide/html/cpovrasynchronousprogrammingoverview.htm

非同期呼び出し完了のポーリング
BeginInvoke によって返された IAsyncResult の IsCompleted プロパティを使用して、非同期呼び出しが完了したことを検出できます。この方法は、ユーザー インターフェイスにサービスを提供するスレッドから非同期呼び出しを行う場合に使用します。完了をポーリングする場合、ユーザー インターフェイス スレッドではユーザー入力の処理を継続できます。


うーん、この例はなんとも微妙な感じがするのですが…
このコード例を出しながら「ユーザー インターフェイス スレッドではユーザー入力の処理を継続できます。」というのはなんか変な気がします。

いま、改めて動作を確認したりしているわけではないんですが、Sleepメソッド中はUIにアクセスできる(UIのイベントを処理できる)というような器用なことはないと思います(DoEventsとか使えば別だと思いますが)。
# UIスレッドといっても、現在実行中の(つまりSleepを呼び出して
# 停止しようとしている)スレッドそのものなわけで。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2003-10-06 18:27
引用:

なちゃさんの書き込み (2003-10-06 17:46) より:
引用:

これを見る限り、Sleepメソッド中はUIにアクセスできそうな気がするのですが・・・。
ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1041/cpguide/html/cpovrasynchronousprogrammingoverview.htm

非同期呼び出し完了のポーリング
BeginInvoke によって返された IAsyncResult の IsCompleted プロパティを使用して、非同期呼び出しが完了したことを検出できます。この方法は、ユーザー インターフェイスにサービスを提供するスレッドから非同期呼び出しを行う場合に使用します。完了をポーリングする場合、ユーザー インターフェイス スレッドではユーザー入力の処理を継続できます。


うーん、この例はなんとも微妙な感じがするのですが…
このコード例を出しながら「ユーザー インターフェイス スレッドではユーザー入力の処理を継続できます。」というのはなんか変な気がします。

いま、改めて動作を確認したりしているわけではないんですが、Sleepメソッド中はUIにアクセスできる(UIのイベントを処理できる)というような器用なことはないと思います(DoEventsとか使えば別だと思いますが)。
# UIスレッドといっても、現在実行中の(つまりSleepを呼び出して
# 停止しようとしている)スレッドそのものなわけで。



 まぁ、難解なドキュメントはMSのお得意ということで。。。

 「UIはad.TestMethodで実行される」と考えると、どうでしょう?待つのは「UIスレッドが終わったかどうかを判定するスレッド」なので、当然UIのイベントは処理できるわけです。ポーリングはWileループのことですから、これがなければいきなりEndInvokeにいってしまい、スレッドが終了させられるか、EndInvokeの処理(UIスレッドが終了するのを待つという処理)の為にCPU時間がとられ、UIスレッドが止まってしまいます。
1

スキルアップ/キャリアアップ(JOB@IT)