6

2 つのスレッド間のあいまいなロックアップは、グローバル インタープリター ロックまたはその他の「舞台裏のロック」に関連しているようで、トラブルシューティングを続行する方法がわかりません。ロックアップを解消するためのヒントをいただければ幸いです。

この問題は、より大きなコード セットで再現されます (不規則かつランダムに発生します)。コードは厳密に python です。Python のバージョンは 2.6.5 (Linux 上) です。何時間ものトラブルシューティングにより、ロックアップが発生したときの問題が次のように軽減されました。

  1. プログラムには実行中のスレッドが 2 つしかありません
  2. スレッドは、単一のスレッド化によって保護されたメソッドを同時に呼び出します.RLock
  3. スレッド 1 は、acquire() を介してロック [およびその他のロック] を取得しました
  4. スレッド 2 は acquire() を呼び出しており、ロックを待機していることが確認されています
  5. スレッド 1 は、print() を使用してコンソールに出力できますが、単純なノンブロッキング ライブラリ呼び出しによってロックされています。

#5 の攻撃的な呼び出しは関数 unicode.encode であり、これはノンブロッキングでなければなりません。スレッドがロックする位置のスレッド 1 の次のコードは、(予想どおり) 'A' と 'B' を出力します。

print('A')
print('B')

ただし、次の例では「A」のみが出力され、スレッドがブロックされます。

print('A')
u'hello'.encode('utf8') # This dummy (non-blocking) call locks up Thread 1
print('B')

これは私にはまったく意味がありません。2 つのスレッド間に論理的なデッドロック状態はありません。スレッド 1 は、RLock の取得を黙って待機しているスレッド 2 にまったく干渉しないノンブロッキング ライブラリ呼び出しによってブロックされています。スレッド 1 がブロックされている唯一の原因は、スレッド 1 が GIL を待っていることです。

これをさらにトラブルシューティングする方法、または回避策として GIL 操作を制御または操作するメカニズムはありますか?

編集: samplebias に対応する追加情報 (および返信ありがとうございます)。この問題は、2 つのスレッド間のタイミングを乱す可能性のあるものに非常に敏感であるように思われるため、トレースの取得に問題がありました。ただし、-f オプションのみを指定して strace を実行すると、数回繰り返した後、トレースを取得できました。

スレッド 1 には、「CHECK_IN」と「CHECK_TEST」の 2 行をコンソールに出力する必要がある次の 3 つのデバッグ ステートメントが含まれています。

print('CHECK IN')#DEBUG
u'hello'.encode('utf8')
print('CHECK TEST')#DEBUG

strace の最後のページは次のとおりです。

8605  mmap2(NULL, 266240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb753d000
8605  mmap2(NULL, 8392704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xb6d3c000
8605  mprotect(0xb6d3c000, 4096, PROT_NONE) = 0
8605  clone(child_stack=0xb753c494, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xb753cbd8, {entry_number:6, base_addr:0xb753cb70, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb753cbd8) = 8606
8606  set_robust_list(0xb753cbe0, 0xc <unfinished ...>
8605  futex(0xa239138, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
8606  <... set_robust_list resumed> )   = 0
8606  futex(0xa239138, FUTEX_WAKE_PRIVATE, 1) = 1
8605  <... futex resumed> )             = 0
8606  gettimeofday( <unfinished ...>
8605  futex(0xa272398, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
8606  <... gettimeofday resumed> {1301528807, 326496}, NULL) = 0
8606  futex(0xa272398, FUTEX_WAKE_PRIVATE, 1) = 1
8605  <... futex resumed> )             = 0
8606  futex(0xa272398, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
8605  gettimeofday( <unfinished ...>
8606  <... futex resumed> )             = 0
8605  <... gettimeofday resumed> {1301528807, 326821}, NULL) = 0
8606  futex(0xa272398, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
8605  futex(0xa272398, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
8606  <... futex resumed> )             = -1 EAGAIN (Resource temporarily unavailable)
8605  <... futex resumed> )             = 0
8606  gettimeofday( <unfinished ...>
8605  futex(0xa272398, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
8606  <... gettimeofday resumed> {1301528807, 326908}, NULL) = 0
8606  futex(0xa272398, FUTEX_WAKE_PRIVATE, 1) = 1
8605  <... futex resumed> )             = 0
8606  futex(0xa272398, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
8605  futex(0xa1b0d70, FUTEX_WAIT_PRIVATE, 0, NULL <unfinished ...>
8606  <... futex resumed> )             = 0
8606  stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=2225, ...}) = 0
8606  fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
8606  mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb6d3b000
8606  write(1, "CHECK IN\n", 9)         = 9
8606  futex(0xa115270, FUTEX_WAIT_PRIVATE, 0, NULL

プログラムがロックする前の 3 行のコードの出力は、次のとおりです。

CHECK IN

したがって、strace は、スレッド 1 (#8606) が 'CHECK_IN' 文字列を書き込む方法を示しており、unicode.encode 呼び出しに到達すると、決して返されない待機状態になります。

ところで、私はすべてのモジュールでいくつかの将来のインポートを作成して、いくつかの新しい Python 規則を維持しています ...

from __future__ import print_function, unicode_literals

...しかし、特にu'hello'文字列がUnicode文字列として明示的に呼び出されているため、それらが違いを生むはずがありません。

4

1 に答える 1

3

ブロックの原因となる Python ソースは何も見つかりませんでしたunicode.encode()。これを再現するために作成したダミー プログラムは、期待どおりに実行されます。スレッド 1 が複数のロックを取得したとおっしゃいましたが、それらのロックをロックアップの原因として排除しましたか?

以下のテスト ケースは、お客様の環境で同じロックアップを示していますか?

import time
import threading

def worker(tid):
    _lock.acquire()
    if not tid:
        # wait for rest of threads to enter acquire
        time.sleep(0.5)    
    print('%d: A' % tid)
    u'hello'.encode('utf-8')
    print('%d: B' % tid)
    _lock.release()

def start(tid):
    th = threading.Thread(target=worker, args=(tid,))
    th.start()
    return th

_num = 2
_lock = threading.RLock()
workers = [start(n) for n in range(_num)]
while all(w.isAlive() for w in workers):
    time.sleep(1)

出力:

0: A
0: B
1: A
1: B

プログラムを実行straceして、プロセスがブロックされている場所を特定することもできます。たとえば、上記のスクリプトでは次のようになります。

% strace -fTr -o trace.out python lockup.py

この-o trace.outフラグは、出力をファイルに書き込むように strace に指示します。それを省略すると、strace は stderr に出力されます。

の内容にtrace.outは、プログラムによって行われたすべてのシステム コールが表示されます。各行には、スレッド ID とシステム コール間の相対時間が先頭に付けられます。行の終わりには、そのシステムコール内で費やされた時間が含まれます。対応する Python コードを使用して、最後のいくつかの syscall に注釈を付けました。

# thread 0 time.sleep(0.5) completes
24778      0.500124 <... select resumed> ) = 0 (Timeout) <0.500599>
# preparing to print()
24778      0.000071 fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 <0.000017>
24778      0.000058 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8fe90a6000 <0.000018>
# print("0: A\n")..
24778      0.000079 write(1, "0: A\n", 5) = 5 <0.000023>
24778      0.000106 write(1, "0: B\n", 5) = 5 <0.000056>
# thread 0 _lock.release()
24778      0.000114 futex(0xe0f3c0, FUTEX_WAKE_PRIVATE, 1) = 1 <0.000024>
24778      0.000108 madvise(0x7f8fe7266000, 8368128, MADV_DONTNEED) = 0 <0.000030>
# thread 0 exit
24778      0.000072 _exit(0)            = ?
# thread 1 _lock.acquire()
24779      0.000050 <... futex resumed> ) = 0 <0.500774>
# thread 1 print("1: A\n") and so on..
24779      0.000052 write(1, "1: A\n", 5) = 5 <0.000026>
24779      0.000086 write(1, "1: B\n", 5) = 5 <0.000026>
24779      0.000099 madvise(0x7f8fe6a65000, 8368128, MADV_DONTNEED) = 0 <0.000024>
24779      0.000064 _exit(0)            = ?
24777      0.499956 <... select resumed> ) = 0 (Timeout) <1.001138>
24777      0.000132 rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f8fe8c7c8f0}, {0x4d9a90, [], SA_RESTORER, 0x7f8fe8c7c8f0}, 8) = 0 <0.000025>
# main thread process exit
24777      0.002349 exit_group(0)       = ?
于 2011-03-30T18:04:10.830 に答える