8

Cocoa の「performSelectorOnMainThread:」メソッドの低レベルの実装の詳細について、誰かが知っているか、議論している優れたドキュメントへのポインタを持っているかどうか疑問に思っていました。

私の推測では、おそらくかなり近いと思いますが、mach ポートまたはその上にある抽象化を使用してスレッド内通信を提供し、mach メッセージの一部としてセレクター情報を渡すことです。

右?違う?ありがとう!

更新 09:39AMPST

答えてくれたEvan DiBiaseとMeckiに感謝しますが、明確にするために、実行ループで何が起こるかは理解していますが、答えを探しているのは; 「メソッドはどこでキューに入れられますか?セレクター情報はどのようにキューに渡されますか?」Apple のドキュメント情報以上のものを探しています。

更新 14:21PST

Chris Hanson はコメントで良い点を指摘しています。ここでの私の目的は、自分のコードでそれらを利用するために、基礎となるメカニズムを学習することではありません。むしろ、コードを実行するように別のスレッドに信号を送るプロセスの概念をよりよく理解することに興味があります。私が言ったように、私自身の調査では、IPC がスレッド間でセレクター情報を渡すためにマッハ メッセージングを利用していると信じていますが、何が起こっているのかについての具体的な情報を具体的に探しているので、理解していると確信できます。物事正しく。ありがとう!

更新 03/06/09

私はこの質問に報奨金を出しました。なぜなら、この質問に回答してもらいたいからです。収集しようとしている場合は、現在提出されているすべての回答、これらの回答と私の元の質問の両方へのコメントを含むすべて必ず読んでください。 、および私が上に投稿した更新テキスト。などで使用されているメカニズムの最低レベルの詳細を探しています。performSelectorOnMainThread:前述したように、Mach ポートと関係があると思われますが、確実に知りたいです。与えられた答えが正しいことを確認できない限り、報奨金は授与されません。みんな、ありがとう!

4

4 に答える 4

10

はい、Machポートを使用します。何が起こるかこれは:

  1. 実行情報(ターゲットオブジェクト、セレクター、セレクターへのオプションのオブジェクト引数など)をカプセル化するデータのブロックは、スレッドの実行ループ情報にエンキューされます。これは、を使用して実行され@synchronized、最終的にはを使用しpthread_mutex_lockます。
  2. CFRunLoopSourceSignalは、ソースを起動する準備ができていることを通知するために呼び出されます。
  3. CFRunLoopWakeUpは、メインスレッドの実行ループにウェイクアップの時間であることを知らせるために呼び出されます。これは、mach_msgを使用して行われます。

Appleドキュメントから:

バージョン1のソースは、実行ループとカーネルによって管理されます。これらのソースは、Machポートを使用して、ソースが起動する準備ができたことを通知します。メッセージがソースのMachポートに到着すると、ソースはカーネルによって自動的に通知されます。メッセージの内容は、ソースが起動されたときに処理するためにソースに渡されます。CFMachPortおよびCFMessagePortの実行ループソースは、現在バージョン1ソースとして実装されています。

私は今スタックトレースを見ています、そしてこれはそれが示すものです:

0 mach_msg
1 CFRunLoopWakeUp
2 -[NSThread _nq:]
3 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:]
4 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:]

mach_msgにブレークポイントを設定すると、確認できるようになります。

于 2009-03-06T20:30:09.390 に答える
2

NSObject のperformSelectorOnMainThread:withObject:waitUntilDone:メソッドのドキュメントには次のように書かれています。

このメソッドは、デフォルトの実行ループ モード ( NSRunLoopCommonModes定数に関連付けられたモード) を使用して、メイン スレッドの実行ループでメッセージをキューに入れます。通常の実行ループ処理の一部として、メイン スレッドはメッセージをデキューし (デフォルトの実行ループ モードのいずれかで実行されていると仮定)、目的のメソッドを呼び出します。

于 2008-09-29T16:21:12.000 に答える
2

もう1つの編集:

コメントの質問に答えるには:

スレッド間で情報を渡すためにどの IPC メカニズムが使用されていますか? 共有メモリ?ソケット?マッハのメッセージ?

NSThread はメイン スレッドへの参照を内部的に格納し、その参照を介してそのスレッドの NSRunloop への参照を取得できます。NSRunloop は内部的にリンクされたリストであり、NSTimer オブジェクトをランループに追加することによって、新しいリンクされたリスト要素が作成され、リストに追加されます。したがって、実際にはメイン スレッドに属しているリンク リストは、別のスレッド内から単純に変更された共有メモリであると言えます。リンクされたリストの編集がスレッドセーフであることを確認するミューテックス/ロック (おそらく NSLock オブジェクトでさえも) があります。

擬似コード:

// Main Thread

for (;;) {
    lock(runloop->runloopLock);
    task = NULL;
    do {
        task = getNextTask(runloop);
        if (!task) {
            // function below unlocks the lock and
            // atomically sends thread to sleep.
            // If thread is woken up again, it will
            // get the lock again before continuing
            // running. See "man pthread_cond_wait"
            // as an example function that works
            // this way
            wait_for_notification(runloop->newTasks, runloop->runloopLock);
        }
    } while (!task);
    unlock(runloop->runloopLock);
    processTask(task);
}


// Other thread, perform selector on main thread
// selector is char *, containing the selector
// object is void *, reference to object

timer = createTimerInPast(selector, object);
runloop = getRunloopOfMainThread();
lock(runloop->runloopLock);
addTask(runloop, timer);
wake_all_sleeping(runloop->newTasks);
unlock(runloop->runloopLock);

もちろん、これは単純化しすぎており、ほとんどの詳細はここでは関数間に隠されています。たとえば、getNextTask は、タイマーが既に起動している必要がある場合にのみ、タイマーを返します。すべてのタイマーの起動日がまだ未来であり、処理する他のイベント (UI からのキーボード、マウス イベント、または送信された通知など) がない場合、NULL が返されます。


私はまだ質問が何であるかわかりません。セレクターは、呼び出されるメソッドの名前を含む C 文字列にすぎません。すべてのメソッドは通常の C 関数であり、メソッド名を文字列および関数ポインタとして含む文字列テーブルが存在します。これは、Objective-C が実際にどのように機能するかの非常に基本的なものです。

以下に書いたように、ターゲット オブジェクトへのポインターとメソッド名を含む C 文字列へのポインターを取得する NSTimer オブジェクトが作成され、タイマーが起動すると、文字列テーブルを使用して呼び出す正しい C メソッドを見つけます (したがって、メソッドの文字列名が必要です) ターゲットオブジェクトの (したがって、それへの参照が必要です)。

正確には実装ではありませんが、それにかなり近いです:

Cocoa のすべてのスレッドには NSRunLoop があります (常にそこにあり、スレッドを作成する必要はありません)。PerformSelectorOnMainThread は、このような NSTimer オブジェクトを作成します。これは、一度だけ起動し、起動する時間がすでに過去にある場合 (したがって、すぐに起動する必要があります)、メイン スレッドの NSRunLoop を取得し、そこにタイマー オブジェクトを追加します。メインスレッドがアイドル状態になるとすぐに、Runloop で処理する次のイベントを検索し (または、処理するものが何もない場合はスリープ状態になり、イベントが追加されるとすぐに再び起動されます)、それを実行します。呼び出しをスケジュールするときにメインスレッドがビジーである場合、現在のタスクが終了するとすぐにタイマーイベントを処理するか、現在スリープ状態である場合、イベントを追加することによって起動されますそしてすぐに処理します。

Apple がどのようにそれを行っている可能性が最も高いかを調べるための良い情報源(クローズド ソースであるため、誰も確実に言うことはできません) は GNUStep です。GCC は Objective-C を処理できるため (これは Apple が出荷する単なる拡張ではなく、標準の GCC でさえ処理できます)、しかし、Apple が出荷するすべての基本クラスなしで Obj-C を使用することはかなり役に立たないため、GNU コミュニティは再試行しようとしました。 - Mac で使用する最も一般的な Obj-C クラスを実装し、それらの実装は OpenSource です。

ここでは、最近のソース パッケージをダウンロードできます。

それを解凍し、詳細については NSThread、NSObject、および NSTimer の実装を見てください。おそらく Apple は gdb を使ってそれを証明できると思いますが、なぜ彼らはそのアプローチと大きく違うことをするのでしょうか? それは非常にうまく機能する巧妙なアプローチです:)

于 2008-09-29T16:27:13.373 に答える
0
于 2008-09-29T23:20:33.037 に答える