• 締切済み

非同期プログラミングは必ずマルチスレッド?

非同期プログラミングは必ずマルチスレッドプログラミングになりますか? ここでいう非同期プログラミングとは、 何かのメソッドを実行してその処理(処理Aとする)の結果を待たずに次の処理Bを実行できて、処理Aが終わったらコールバックやデリゲートで、あらかじめ決められたメソッド(finishとする)が呼ばれるといったものです。 処理Aを実行するメソッドを呼ぶ ↓ すぐに処理Bを実行開始(このときバックグラウンドで処理Aが走っている) ↓ 処理Aが終了したのでfinishメソッドが実行される 例えば、Objective-CのNSURLConnectionで非同期通信するときのようなやつです。 こういった非同期プログラミングは、必ずマルチスレッドを使うことになりますか? 普通、別スレッドで処理させるときはスレッド用のライブラリを使うなどして明示的にマルチスレッドプログラミングをしますが、上のように非同期のメソッドを使うと、その裏で自動的に別のスレッドが動くのでしょうか。 それとも、単一スレッドのみで、非同期プログラミングできるでしょうか。 なお、特殊な言語やOSによっては、いくらでも可能性があると思いますので、 メジャーな環境(Windowsや、スマホ) のみに限定してお願いします。言語で言うと、C、C++、Objective-C、Javaあたり。 また、上の「処理A」が終わったとき、メソッド「finish」が呼ばれるとすると、 処理Bの実行中にどのようなタイミングでfinishが呼ばれるのでしょうか。 処理Bはどのような形でfinishに切り替わるのでしょうか。いきなりfinishに処理を奪い取られるのでしょうか

みんなの回答

  • nak777r
  • ベストアンサー率36% (49/136)
回答No.7

最後に一つだけ 私が説明した内容が解釈の違いだけで同期プログラムですというのであれば、 そう定義してもらってもかまいません ところで、私がNo5,No6で提示した方法を使えば、例えば VBのインタプリタの ような物も作れます VBのソースファイルを読み込み、展開、解析して1行に相当する分だけ処理 をして関数を中断し別の処理をして、次の1行に相当分だけ処理をするような プログラムです さて、私が提示した物は、質問者殿がおっしゃる限り同期プログラムです。 でも、VBのソースは、非同期プログラムです、 もし、このVBのソースを最初からリソースに組み込んで、一つのEXEにした 場合、これは、同期プログラムでしょうか、非同期プログラムでしょうか VBのソースが動きを決める為、これは非同期プログラムと言えるでしょう 同期プログラムであるというなら、本家VBで作ったプログラムも同期プログラムです でも、私の提示したプログラムは、シングルスレッドで動作します。 となれば、一番最初に質問者さんが提示した、非同期プログラムでシングルスレッド は、結局ありえる話に落ち着きます

vtokyo
質問者

補足

何度もありがとうございました。その後いろいろ調べましたが、まずトピタイトルの結論はNOが正解のようです。このサイトがわかりやすかったです。 http://akadav.sourceforge.net/twistedhowto/async.html そもそも私の疑問は、このサイトの「ノンブロッキング・コール」の節に書かれている1,2,3のうちの3の「接続のメカニズムがメール送信関数に対し準備ができた旨の通知をします」という処理(処理Bとする)が、単一スレッドでなぜできるのか、ということに尽きます。1の処理(関数の呼び出し)を実行したスレッドは、即座にその関数の戻りを受けた後、何らかの別の処理(処理Cとする)を進めているはずです。CPUはその処理Cにかかりっきりになっているのに、一体どうして処理Bを実行できるのか(祖レッドは一つしかないのに)。それが疑問なので、非同期は必ず別スレッドを使うものなのかなと思った次第です。

  • nak777r
  • ベストアンサー率36% (49/136)
回答No.6

補足なので、先に 回答 NO.5 を見てください 先程の回答で、 Aの関数を FormShow としましょう とした部分で、この処理が時間がかかる場合 構造体に内部番号てきな物をもたせておいて 10ステップ程実行して、構造体に情報をセットし code をFormShowのコードで次回に処理を回します 例えば、表示するフォーム的には ボタンに見えるパーツの物、テキストに見える パーツの物等があるのなら、 ボタンに見えるパーツを表示する code と構造体 テキストに見えるパーツを表示する code と構造体 等を追加して、さらに、自分自身の code と構造体 を追加して、関数を抜けます その後の FormShow の関数では、それぞれのパーツの 表示が完了しているかを監視して、全て正常に 書かれたなら 以降は、FormShow の code の追加は しません 次のステップの関数となる code を追加します なので、 FormShow という関数としては ここで、正常終了を返して、関数が終了した事になります 関数が返るまで次の事が出来ないのが同期プログラム なので、このプログラムは非同期プログラムといえます

  • nak777r
  • ベストアンサー率36% (49/136)
回答No.5

どうやら質問者さんが間違っているのは 同期プログラムの定義のようですね 非同期のプログラムとは処理がいろいろ出来るプログラムの事です 印刷をしながらメインの画面を切替える 長い時間かかるソートの処理をしながら別の操作が行える等です 通常、ソートの処理も印刷の処理も1本の関数で行われます、 関数は呼び出されると、 処理が正常終了か異常終了で関数が戻ります 印刷であれば、1ページ丸まる印刷が終了するか エラー、又は、中断のトリガーが立って印刷を中止したら 関数がその結果を正常終了か、異常終了かで関数が戻ります で、 関数が戻ってくるまで、他の処理が出来ないのが同期通信で 他の事は出来ませんが、非同期プログラムは他の処理が行えます という風に習ったのであれば、それは正しいですが 関数が、正常終了でも異常終了でもなく 一旦停止で返ってこれば、他の処理も出来る訳で それは、非同期プログラムといえるわけです そうか、過去の回答で、 同じ関数を何回もやり取りする事でたとえましたが 別に同じ関数である必要も無いですね、 過去のAという関数でという風に記述していましたが いろいろと名前をつけていきます まず、親元のプログラムは、構造体の配列を持っています 構造体は、{ int code; void* arg } の配列とします で、配列の中から順番に取り出してきて、code を元に関数を振分けるのが 主な仕事とします while(true)で回し switchで code の内容を元に 関数を呼び出す作りでしょうか では、過去の回答でAとしていた関数 最初のAの関数を newForm としましょう 呼出し元は、code から newForm を呼び出しました newForm は、メモリを確保して、フォームの情報の構造体を 定義して、呼出し元の構造体の配列の末尾に 次の code と フォーム情報の構造体を void* argにキャストして 配列の最後に追加します で、次のAの関数を FormShow としましょう 呼出し元のプログラムは、code から FormShow を呼び出しました FormShow は、フォームの情報を元にフォームを表示し 次の code と void* argにその情報をキャストして配列の最後に追加します で、次のAの関数を FormIdle としましょう 呼出し元のプログラムは、code から FormIdle を呼び出しました FormIdle は、このフォームに対して何かアクションが行われて いないかチェックします、何も無ければ 再度 FormIdleの code とフォームの情報を void* argに渡して抜けます 何かのアクションがあれば、 その code とフォームの情報を追加 さらに、FormIdleの code とフォームの情報を追加して抜けます こういう作りの流れの中での1行分の印刷バッファに貯めて、それを void* arg に 渡して関数を抜けます、という処理を過去の回答で書いたつもりです 関数の中で、印刷バッファが、1ページ分にたまったらプリンタに送信する処理をして その後は、 code も arg も必要ないので追加せずに抜けるという流れになります

  • nak777r
  • ベストアンサー率36% (49/136)
回答No.4

>ご丁寧な解説ありがとうございます。ただそれは同期になるのではないでしょうか。 >同期非同期の定義にもよりますけど、私が理解している非同期とは、呼び元が何らかの関数を呼んだとき、 >その返りを待たずに別の処理に進むこと、と思ってます。例えば呼び元がループだとしても、何かの関数を >呼ぶと、そのからリターンが返るまでは一応待ち、その間はループが停止されているときは同期だと思うのですが、どうでしょうか。 ですから、 呼出し元が、何らかのタイミングで関数Aを読んだとして、 その関数Aの最終的な返り値を得るのが、 引数で得る内部番号100になった時だとすれば それまでの間に当然別の関数Bの処理も行っています 親元のループのタイミングで、1秒間隔で関数Xを呼ぶという事も出来ます getch()等でキーボード入力を親元のループで確認して、 スペースが押されたタイミングで関数Cの起動をかけることもできます (1秒間隔もキーボードも若干のタイムラグ有りですが) 印刷完了が処理の返りとするなら、 1行印刷で関数を抜けるを繰り返せば 印刷中に他の処理が出来ないという事もありません ・関数を呼ぶ、一瞬で返ってくる、 ・他の関数を呼ぶ、一瞬で返ってくる、 ・キーボードが押されたので、キー判定の関数を呼ぶ一瞬で帰ってくる ・他の関数を呼ぶ、一瞬で返ってくる、 ・印刷の関数を呼ぶ、1行だけ印刷する ・他の関数を呼ぶ、一瞬で返ってくる、 ・最初の関数を呼ぶ、一瞬で返ってくる、 ・1秒経過のタイミングなので、タイマーの関数を呼ぶ一瞬で返ってくる、 ・他の関数を呼ぶ、一瞬で返ってくる、 ・印刷の関数を呼ぶ、1行だけ印刷する ・最初の関数を呼ぶ、最終的な結果が得られる これらの並びは、順番どおり(同期)では無いですよね 最終的な結果が取得できるタイミングも違います

vtokyo
質問者

補足

ありがとうございます。が、やはりそれも同期のように思えてしまうんです(同期の定義の問題かもしれませんが)。見かけ上、印刷関数と他の関数が同時進行しているように見えますが、1行印刷の関数を実行したとき、他の関数を呼ぶのは、その1行印刷関数が戻ってきてから(終了してから)になりますよね。  「その関数Aの最終的な返り値を得るのが、引数で得る内部番号100になった時だとすれば、それまでの間に当然別の関数Bの処理も行っています」の記述ですが、例えばそのAはこんな感じでしょうか。 func_A(n) { if( n == 100 ) { return 最終的な返り値; else { ココ } }  言われていることは、上の「ココ」のときに、Aを終了せずに、Aとは違う他のB関数を実行する、ということですか、その場合は確かに非同期ですが、ココのところでAを抜けずにどうやって(同一プロセスの同一スレッドで)、別の関数BにCPUを渡せるのかわかりません同一プロセスの同一スレッドだけの処理を考えていますので、タイマー割り込みなどで他のプロセス、他のスレッドが並列実行される、ということであれば、最初の質問の通り、それはマルチスレッドになってしまいますよね。  あるいは、もしココのところでreturnするとかAをいったん抜けるなら、それは普通の同期プログラムじゃないでしょうか。

  • nak777r
  • ベストアンサー率36% (49/136)
回答No.3

>バックグラウンドは何もない状態で「非同期」というのが >イメージがわかないのですが、 >例えばどのような処理のことでしょうか。 違いは、スレッドではなく関数であるという事だけなので 説明不要だと思ったのですが・・・ 関数を開始、関数を終了、関数を開始、関数を終了、・・・ を繰り返すイメージでスレッドと同じような事をする方法を 考えてくださいという事です やり方は、いろいろありますが、例えば、 関数が呼ばれた時、引数の内容で関数は、初回か確認したり 何の処理を行うかを判断して、その処理を行います、 関数を抜けるときに、次回行う処理の番号なりメモリなり を外部変数の次回処理を行う配列の末尾セットして関数を 終了する 呼出し元は、常時ループをしながら、次回処理を行う配列の 先頭の情報を取得し、その処理に該当する関数を情報を引数に 呼び出す、という事を繰り返すだけです 呼び出される関数は、例えば、引数にて初回と判断したら、 使用する構造体を動的確保、及び初期化だけして、戻り値に そのポインタをセットして関数を抜ける そして、次回呼ばれた時に、引数で受取ったポインタから構造 体を展開し、構造体の内容を元に処理を行ったり処理を中断し たりして戻り値にポインタを渡し次回に処理を回す という事を繰り返すイメージで考えられると思います。

vtokyo
質問者

補足

ご丁寧な解説ありがとうございます。ただそれは同期になるのではないでしょうか。 同期非同期の定義にもよりますけど、私が理解している非同期とは、呼び元が何らかの関数を呼んだとき、 その返りを待たずに別の処理に進むこと、と思ってます。例えば呼び元がループだとしても、何かの関数を 呼ぶと、そのからリターンが返るまでは一応待ち、その間はループが停止されているときは同期だと思うのですが、どうでしょうか。

  • nak777r
  • ベストアンサー率36% (49/136)
回答No.2

>非同期プログラミングは必ずマルチスレッドプログラミングになりますか? この質問には、「必ずでは無いです。」と答えますが、 >ここでいう非同期プログラミングとは、( ~ 中略 ~ ) >すぐに処理Bを実行開始(このときバックグラウンドで処理Aが走っている) 自分で発言していて気づきませんか? バックグラウンドで処理Aが走っている = 処理Aのスレッド 処理Bを実行開始 =処理Bのスレッド なので、この考え方だと、「必ずマルチスレッドになります。」 シングルスレッドで、非同期な処理を行うのであれば そういう作り方をしなさいという事です

vtokyo
質問者

補足

ありがとうございます。質問の仕方が悪かったですね。 バックグラウンドは何もない状態で「非同期」というのがイメージがわかないのですが、 例えばどのような処理のことでしょうか。

回答No.1

> 非同期プログラミングは必ずマルチスレッドプログラミングになりますか? 場合によりけり。 Windowsなんかだと、非同期APIを呼び出すとすぐさま戻ってきます(おシゴトは裏でやってんだけども)。 んでもっておシゴトが完了したらWindowsメッセージが飛んでくるので、そのメッセージを定期的に監視してればいい。 > 単一スレッドのみで、非同期プログラミングできるでしょうか。 "からくり"によります。 ひとつのスレッドで短時間にタスクを切り替えながら複数のおシゴトをすることができますから。 Windows3.xはそうやってましたね。ひとつのお仕事がCPUを占有しないよう、ちょっと動いては一旦OSに制御を返してました。

関連するQ&A