2017-11-15 9 views
1

Python C APIを使用してエラーが表示されないという問題が発生しています。Python C API:いくつかのPyObjectを作成した後のWindowsError

背景:私はしばらくの間、ネイティブコード(C++)を実行するために​​を使用してきたが、今まで、私は実際にPythonのC APIを使用して、特定の何をやったことがなかったです。私は主にPythonから構造体を渡し、C++から構造体を埋めていました。私が構造体を使用していた方法は面倒になってきたので、私はPythonオブジェクトをC++で直接作成し、Pythonスクリプトに渡してみることに決めました。

コード: 私は一つだけの機能でDLL(Foo.dll)を持っている:

#define N 223 
__declspec(dllexport) void Bar(void) 
{ 
    std::cout << "Bar" << std::endl; 

    for (int i = 0; i < N; ++i) 
    { 
     auto list = PyList_New(0); 
     std::cout << "Created: " << i << std::endl; 
     //Py_DECREF(list); 
    } 
} 

そして私は私が実行しているPythonスクリプトがあります:何が起こる

import ctypes as C 
dll = r"C:\path\to\dll\Foo.dll" 
Foo = C.CDLL(dll) 
# lists = [[] for _ in range(0)] 
Foo.Bar() 
print "Done." 

を:上記のDLLのN222以下と定義すると、コードは正常に動作します(ただし、メモリリークは例外ですが、問題ではない)。

//Py_DECREF(list)行のコメントを外すと、コードは正常に動作します。

ただし、上記のコードで、私はこの取得:実際には

Bar 
Created: 0 
Created: 1 
Created: 2 
...(omitted for your sake) 
Created: 219 
Created: 220 
Created: 221 
Traceback (most recent call last): 
    File "C:\path_to_script\script.py", line 9, in <module> 
    Foo.Bar() 
WindowsError: exception: access violation reading 0x00000028 

を、私は上の辞書、リスト、タプルとし、これと同じ結果を得ます。リストを作成して空のサブリストをそのリストに追加すると、同じ結果が得られます。

私は実際のPythonスクリプト内から作成するすべてのリストは、DLLがこのウィンドウエラーを取得する前に行うことができるリストの数を減らします。

私はPythonスクリプトで222以上のリストを作成すると、さらに悪いことに、DLLは720 のようなものが作成されるまでこのエラーに遭遇しません。

**その他の詳細:**

  • Anaconda2 32ビットのPython 2.7配布その分布
  • python.exeからPython.hpython27.libを使用
  • を(使用してWindows 10 - バージョン:2.7.13 :: Anaconda custom (32-bit)
  • Visual StudioでDLLを作成する2017

C++コードから多くのPyObjectを作成しない限り、すべて正常に動作しているようです。私はPyObjectをPythonコードとの間でやりとりすることができます。そして、私のC++コード内からオブジェクトが "あまりにも多く"作成されるまでは正常に動作します。

何が起こっているのですか?

答えて

3

From the documentation for CDLL

Pythonのグローバルインタプリタロックは、これらのライブラリによってエクスポートされた任意の関数を呼び出す前にリリースされ、その後再取得されます。

これにより、は安全ではありませんはPython C APIコードを使用します。正確にどのように失敗するかは、あなたが探しているときは予測できません。割り振りによってガベージコレクターが起動された場合は、それが関係していると思いますが、正確な原因を解決するのに時間を費やす価値はないと思います。

から選んだのには、2つのソリューション(少なくとも)あります:

  1. (それはGILを解放しないことを除いてCDLLのようなものであるドキュメントノート)の使用ctypes.PyDLL
  2. があなたのC内GILを再取得++コード - これを行う簡単な方法は次のとおりです。

    auto state = PyGILState_Ensure(); 
    // C++ code requiring GIL - probably your entire loop 
    PyGILState_Release(state); 
    
+0

私は両方のソリューションは、pを解決するために表示されていることを確認しました汚れ。彼らは私が実際にドキュメンテーション_を読んでくれることをどうにかしていますか?ありがとう。 – Aaron

+0

私は、「Python C APIで単純なことをやっているときのミステリーエラーは、通常GILによるものかPythonの初期化ではない」という考えから作業を始めました。次に、GILのctypesドキュメントを検索しました。私は実際には詳細な文書も読んでいませんでした。 – DavidW

関連する問題