ずっとWindowsでやってきたので今度はUbuntuで同じことをする。
Ubuntuの場合はdllではなくsoファイルという事になるが、手順などは変わらない。
sysconfig.get_config_varsで各パスを見つける。.aはwindowsの.libに該当。
一覧を出すと長いので引数に値名を入れて必要なものだけ見つける
# where-is-pyh.py import sysconfig import pprint pprint.pprint(sysconfig.get_config_vars('LIBDIR')) # .a ファイルの場所 pprint.pprint(sysconfig.get_config_vars('LIBRARY')) # .a ファイルの名前 pprint.pprint(sysconfig.get_config_vars('INCLUDEPY')) # python.h の場所
Windows版と動的リンクライブラリの設定が若干違う(というかない)が基本的には同じ。DllMainなどは不要なのでその分コードは短い。
// mymod.hpp // このプロジェクトのビルド結果を「sopy.so」とする。 #include <Python.h> // この関数をPython側から呼び出したいとする。 PyObject * hello_in_c(PyObject*); // 第一引数はPython側のself用。 // 関数へアクセスする方法一覧 static PyMethodDef method_list[] = { // Pythonで使用する関数名 , C++内で使用する関数名 , 引数の個数に関するフラグ , docstring { "hello_on_python", (PyCFunction)hello_in_c, METH_VARARGS , nullptr }, // ... 公開したい関数の個数分だけ記述する // 配列の最後は全てNULLの要素を入れておく { nullptr, nullptr, 0, nullptr } }; // モジュールを定義する構造体 static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, "sopy", // モジュール名 "Example of pyhton wrapper", // モジュール説明 0, method_list // Structure that defines the methods of the module }; // 関数名はPyInit_ + soファイル名 PyMODINIT_FUNC PyInit_sopy() { return PyModule_Create(&module_def); }
// mymod.cpp #include "mymod.hpp" PyObject* hello_in_c(PyObject*) { printf("hello world from C"); return Py_None; }
最初のステップでsysconfigで見つけたパスとライブラリ名をそれぞれ指定する。
$ g++ mymod.cpp -shared -fPIC -o sopy.so -I/home/myname/anaconda3/envs/BindTest/include/python3.9 -L/home/myname/anaconda3/envs/BindTest/lib -lpython3.9
import sopy sopy.hello_on_python()
Python側からリストを渡したときの受け取り方。
// mymod.cpp #include "mymod.hpp" void print_int(PyObject* p); void print_float(PyObject* p); void print_string(PyObject* p); PyObject* list_in_c(PyObject*, PyObject* args) { // 受け取ったリストの内容をlistargsへ入れる PyObject* listargs; if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &listargs)) return NULL; // リストの要素数を取得 int count = PyList_Size(listargs); printf("list item count == %d\n", count); // リストのアイテムを取得 PyObject* val_0 = PyList_GetItem(listargs, 0); PyObject* val_1 = PyList_GetItem(listargs, 1); PyObject* val_2 = PyList_GetItem(listargs, 2); //各要素の型は、PyObject*を _Check 関数にかけて型チェックを行う ////////////////////////////// if ((bool)PyLong_Check(val_0) == true) { print_int(val_0); } else if ((bool)PyFloat_Check(val_0) == true) { print_float(val_0); } else if ((bool)PyUnicode_Check(val_0) == true) { print_string(val_0); } ////////////////////////////// if ((bool)PyLong_Check(val_1) == true) { print_int(val_1); } else if ((bool)PyFloat_Check(val_1) == true) { print_float(val_1); } else if ((bool)PyUnicode_Check(val_1) == true) { print_string(val_1); } ////////////////////////////// if ((bool)PyLong_Check(val_2) == true) { print_int(val_2); } else if ((bool)PyFloat_Check(val_2) == true) { print_float(val_2); } else if ((bool)PyUnicode_Check(val_2) == true) { print_string(val_2); } } void print_int(PyObject* p) { long v = PyLong_AsLong(p); printf("%d\n", v); } void print_float(PyObject* p) { float v = PyFloat_AsDouble(p); printf("%lf\n", v); } void print_string(PyObject* p) { const char* v = PyUnicode_AsUTF8(p); printf("%s\n", v); }
__declspec(dllexport) extern "C" PyObject * list_in_c(PyObject*, PyObject*); // 関数へアクセスする方法一覧 static PyMethodDef method_list[] = { // Pythonで使用する関数名 , C++内で使用する関数名 , 引数の個数に関するフラグ , docstring { "list_function", (PyCFunction)list_in_c, METH_VARARGS , nullptr }, // 配列の最後は全てNULLの要素を入れておく { nullptr, nullptr, 0, nullptr } };
import DLLforPython DLLforPython.list_function([22,"abc",12.3])
ポインタを渡す場合はPyCapsule_Newで包んで渡す。
データがnewされたものなら、一緒にdeleterも渡すことができる。
// C++側の関数宣言 // 配列作成 __declspec(dllexport) extern "C" PyObject * create_array_in_c(PyObject*); // 配列からアイテム取得 __declspec(dllexport) extern "C" PyObject * get_item_in_c(PyObject*, PyObject* args); // 関数へアクセスする方法一覧 static PyMethodDef method_list[] = { { "create_array", (PyCFunction)create_array_in_c, METH_VARARGS , nullptr }, { "get_item", (PyCFunction)get_item_in_c, METH_VARARGS , nullptr }, // 配列の最後は全てNULLの要素を入れておく { nullptr, nullptr, 0, nullptr } };
// mymod.cpp #include "mymod.hpp" #define NAME nullptr // デリータ void array_deleter(PyObject* obj) { long* p = (long*)PyCapsule_GetPointer(obj, NAME); delete [] p; } // データを作成する関数 PyObject* create_array_in_c(PyObject*) { long* p = new long[5]{ 0,1,2,3,4 }; // ポインタをPyObjectへ変換 // 同時にデリータを指定して自動解放を可能にする PyObject* pyo = PyCapsule_New(p, NAME, array_deleter); return pyo; } PyObject * get_item_in_c(PyObject*, PyObject * args) { PyObject* dataptr; int index; // argsから引数取り出し // PyObject* は 'O' を使用 if (!PyArg_ParseTuple(args, "Oi", &dataptr, &index)) return NULL; long* p = (long*)PyCapsule_GetPointer(dataptr, NAME); return PyLong_FromLong(p[index]); }
import DLLforPython array = DLLforPython.create_array() print( DLLforPython.get_item(array,3) )
Python/C APIでは引数を複数渡す場合、第二引数にPython側から渡された引数がすべてまとめられている。それを取り出すためにPyArg_ParseTupleを使うのだが、この時何番目の引数の型が何かを関数へ教えてやる必要がある。
下の例では、それが第二引数の"ii"となっている。
// mymod.hpp
#pragma once #include <Python.h> #pragma comment (lib,"python39.lib")
// C++側の関数宣言 // python側から引数が複数与えられる場合、 // 第二引数に全ての引数がまとめて入っている __declspec(dllexport) extern "C" PyObject * add_in_c(PyObject*, PyObject* xy);
// 関数へアクセスする方法一覧 static PyMethodDef method_list[] = { // Pythonで使用する関数名 , C++内で使用する関数名 , 引数の個数に関するフラグ , docstring { "add", (PyCFunction)add_in_c, METH_VARARGS , nullptr }, // 配列の最後は全てNULLの要素を入れておく { nullptr, nullptr, 0, nullptr } };
// モジュールを定義する構造体 static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, "DLLforPython", // モジュール名 "Example of pyhton wrapper", // モジュール説明 0, method_list // Structure that defines the methods of the module }; PyMODINIT_FUNC PyInit_DLLforPython() { return PyModule_Create(&module_def); }
// mymod.cpp #include "mymod.hpp" PyObject* add_in_c(PyObject*, PyObject* xy) { long a, b;
// 引数xyからPython側から渡された二つの引数を取り出す // 第二引数の "ii" はaがint,bがintを表す int ret = PyArg_ParseTuple(xy, "ii", &a, &b); if (!ret) return NULL; return PyLong_FromLong(a + b); }
import DLLforPython print( DLLforPython.add(2,3) )
データ型によってどのような指定を行うかは以下に書かれている。
https://docs.python.org/ja/3/c-api/arg.html
// C++側の関数宣言 // python側から引数が複数与えられる場合、 // 第二引数に全ての引数がまとめて入っている __declspec(dllexport) extern "C" PyObject * function_in_c(PyObject*, PyObject* xy); // 関数へアクセスする方法一覧 static PyMethodDef method_list[] = { // Pythonで使用する関数名 , C++内で使用する関数名 , 引数の個数に関するフラグ , docstring { "function", (PyCFunction)function_in_c, METH_VARARGS , nullptr }, // 配列の最後は全てNULLの要素を入れておく { nullptr, nullptr, 0, nullptr } };
// mymod.cpp #include "mymod.hpp" PyObject* function_in_c(PyObject*, PyObject* args) { bool b; // PyArg_ParseTupleには b を与える char *s; // PyArg_ParseTupleには s を与える long i; // PyArg_ParseTupleには i を与える float f; // PyArg_ParseTupleには f を与える Py_complex d;// PyArg_ParseTupleには D を与える // 第三引数以降は可変長引数 int ret = PyArg_ParseTuple( args, "bsifD", &b, &s, &i, &f, &d ); printf("bool %s\n", b ? "true" : "false"); printf("char %s\n", s); printf("long %d\n", i); printf("float %lf\n", f); printf("complex %lf+i%lf\n", d.real, d.imag); return Py_None; }
import DLLforPython DLLforPython.function( True, "hello", 5, 4.8, 3+2j )
#pragma once #include <Python.h> // libファイルが必要 // anaconda3\Include にPython.hがある時、おそらく // anaconda3\libs にある。 #pragma comment (lib,"python39.lib")
// C++側の関数宣言 __declspec(dllexport) extern "C" PyObject * get_int_in_c(PyObject*); __declspec(dllexport) extern "C" PyObject * get_bool_in_c(PyObject*); __declspec(dllexport) extern "C" PyObject * get_float_in_c(PyObject*); __declspec(dllexport) extern "C" PyObject * get_string_in_c(PyObject*);
// 関数へアクセスする方法一覧 static PyMethodDef method_list[] = { // Pythonで使用する関数名 , C++内で使用する関数名 , 引数の個数に関するフラグ , docstring { "get_int", (PyCFunction)get_int_in_c, METH_VARARGS , nullptr }, { "get_bool", (PyCFunction)get_bool_in_c, METH_VARARGS , nullptr }, { "get_float", (PyCFunction)get_float_in_c, METH_VARARGS , nullptr }, { "get_string", (PyCFunction)get_string_in_c, METH_VARARGS , nullptr }, // 配列の最後は全てNULLの要素を入れておく { nullptr, nullptr, 0, nullptr } };
// モジュールを定義する構造体 static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, "DLLforPython", // モジュール名 "Example of pyhton wrapper", // モジュール説明 0, method_list // Structure that defines the methods of the module }; PyMODINIT_FUNC PyInit_DLLforPython() { return PyModule_Create(&module_def); }
// mymod.cpp #include "mymod.hpp"
// intを返す PyObject* get_int_in_c(PyObject*) { PyObject* obj = PyLong_FromLong(100); return obj; }
// boolを返す PyObject* get_bool_in_c(PyObject*) { PyObject* obj = PyBool_FromLong((long)false); return obj; }
// floatを返す PyObject * get_float_in_c(PyObject*) { PyObject* obj = PyFloat_FromDouble(3.5); return obj; }
// stringを返す PyObject * get_string_in_c(PyObject*) { PyObject* obj = PyUnicode_FromFormat("test"); return obj; }
import DLLforPython print( DLLforPython.get_int() ) print( DLLforPython.get_bool() ) print( DLLforPython.get_float() ) print( DLLforPython.get_string() )
Python側からintやfloatなどの型を渡す場合、全てPyObject*型として渡されるので、PyLong_AsLongなどの関数を使って実際の値に変換する必要がある。
// mymod.hpp
#pragma once #include <Python.h> // libファイルが必要 // anaconda3\Include にPython.hがある時、おそらく // anaconda3\libs にある。 #pragma comment (lib,"python39.lib")
// C++側の関数宣言 __declspec(dllexport) extern "C" PyObject * display_int(PyObject*, PyObject*); __declspec(dllexport) extern "C" PyObject * display_bool(PyObject*, PyObject*); __declspec(dllexport) extern "C" PyObject * display_float(PyObject*, PyObject*); __declspec(dllexport) extern "C" PyObject * display_string(PyObject*, PyObject*);
// 関数へアクセスする方法一覧 static PyMethodDef method_list[] = { // Pythonで使用する関数名 , C++内で使用する関数名 , 引数の個数に関するフラグ , docstring { "disp_int", (PyCFunction)display_int, METH_O , nullptr }, { "disp_bool", (PyCFunction)display_bool, METH_O , nullptr }, { "disp_float", (PyCFunction)display_float, METH_O , nullptr }, { "disp_string", (PyCFunction)display_string, METH_O , nullptr }, // 配列の最後は全てNULLの要素を入れておく { nullptr, nullptr, 0, nullptr } };
// モジュールを定義する構造体 static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, "DLLforPython", // モジュール名 "Example of pyhton wrapper", // モジュール説明 0, method_list // Structure that defines the methods of the module }; PyMODINIT_FUNC PyInit_DLLforPython() { return PyModule_Create(&module_def); }
// mymod.cpp #include "mymod.hpp"
// intを受け取る PyObject* display_int(PyObject*, PyObject* val) { long value = PyLong_AsLong(val); printf("%d\n", value); return Py_None; }
// boolを受け取る PyObject* display_bool(PyObject*, PyObject* val) { int value = PyObject_IsTrue(val); puts(bool(value) ? "TRUE" : "FALSE"); return Py_None; }
// floatを受け取る PyObject * display_float(PyObject*, PyObject* val) { double value = PyFloat_AsDouble(val); printf("%lf\n", value); return Py_None; }
// stringを受け取る PyObject * display_string(PyObject*, PyObject* val) { const char* value = PyUnicode_AsUTF8(val); printf("%s\n", value); return Py_None; }
import DLLforPython DLLforPython.disp_int(500) DLLforPython.disp_bool(True) DLLforPython.disp_float(1.5) DLLforPython.disp_string('test')
1.Python/C APIで作ったモジュールはただのdllファイルの拡張子をpydに変更したもの
2.Pythonへdll内にある関数一覧を公開するため、dll内にPyInit_pydファイル名 という関数を用意しておく
3.dll側の関数は戻り値がPyObject*,引数も全てPyObject*型にする
以下のスクリプトを実行し、'INCLUDEPY' という値を見つける。
# where-is-pyh.py import sysconfig import pprint pprint.pprint(sysconfig.get_config_vars())
以下のプログラムを書き、DLLとしてビルドする。
注意:モジュール名==dllファイル名 なので、出力ファイル名が DLLforPython.pyd となるようにプロジェクト設定を調整しておく。普通プロジェクト名がそのままファイル名になるので拡張子設定だけを変えればいい。
// mymod.hpp
// このプロジェクトのビルド結果は「DLLforPython.pyd」となる。
// 当然だが.dllで出力して拡張子だけ変えてもいい
#pragma once #include <Python.h> // libファイルが必要 // anaconda3\Include にPython.hがある時、おそらく // anaconda3\libs にある。 #pragma comment (lib,"python39.lib") // この関数をPython側から呼び出したいとする。 __declspec(dllexport) extern "C" PyObject * hello_in_c(PyObject*);
// ↑今回はlibでimportしないので__declspecの切り替えは不要 // 第一引数はPython側のself用。
// 関数へアクセスする方法一覧 static PyMethodDef method_list[] = { // Pythonで使用する関数名 , C++内で使用する関数名 , 引数の個数に関するフラグ , docstring { "hello_on_python", (PyCFunction)hello_in_c, METH_VARARGS , nullptr }, // ... 公開したい関数の個数分だけ記述する
// 配列の最後は全てNULLの要素を入れておく { nullptr, nullptr, 0, nullptr } };
// モジュールを定義する構造体 static PyModuleDef module_def = { PyModuleDef_HEAD_INIT, "DLLforPython", // モジュール名 "Example of pyhton wrapper", // モジュール説明 0, method_list // Structure that defines the methods of the module };
// 関数名はPyInit_ + pydファイル名 PyMODINIT_FUNC PyInit_DLLforPython() { return PyModule_Create(&module_def); }
// mymod.cpp #include "mymod.hpp" PyObject* hello_in_c(PyObject*) { printf("hello world from C"); return Py_None; }
dllmain.cppはVC++でプロジェクトを作ると勝手にできるのでいじらなければそれでいい。
// dllmain.cpp : DLL アプリケーションのエントリ ポイントを定義します。 #include "pch.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
ビルドしたpydファイルをpythonスクリプトと同じディレクトリに置き、テスト用のスクリプトでimportして使用する。
# test.py import DLLforPython DLLforPython.hello_on_python()

デリータの指定なしで作ったshared_ptrからポインタを奪い取ることはどうあがいてもできそうにない。unique_ptrをshared_ptrへ変換することも、uniue_ptrから生ポインタを解放することもできるが、shared_ptrはshared_ptrから変更できない。
#include <memory>
void pattern1() { std::unique_ptr<int> unique_p(new int); // OK 管理を shared_ptrへ変更 std::shared_ptr<int> shared_p = std::move(unique_p); }
void pattern2() { std::unique_ptr<int> unique_p(new int); // OK 管理を自ら行う int* p = unique_p.release(); }
void pattern3() { std::shared_ptr<int> shared_p(new int); // NG releaseは存在しない int* p = shared_p.release(); // NG unique_ptr への変換はできない std::unique_ptr<int> unique_p(std::move(shared_p)); }
これはつまり、「自分以外にshared_ptrの生成権限(業務上)がある場合はどうしようもない」という事を意味する。だから被害者を減らすために、スマートポインタの扱いには注意を払うべきである。
unique_ptrであれば、少なくともshared_ptrに意図的に変えるまではどうとでもできる。他者が使うかもしれない自コードがshared_ptrを生成して返すような真似は、未来に誰かを不幸にする可能性があるので避けるようにする。
#include <iostream> #include <memory>
// unique_ptrを返すFactory。後でいろいろと融通が利く std::unique_ptr<int> Factory_unique() { return std::make_unique<int>(); }
void job_happy() { auto uptr = std::move(Factory_unique()); *uptr = 5; printf("%d\n", *uptr); // OK int* ptr = uptr.release(); // OK std::shared_ptr<int> sptr = std::move(uptr); }
// shared_ptrを返すFactory。融通が利かない std::shared_ptr<int> Factory_shared() { return std::make_shared<int>(); }
void job_unhappy() { auto sptr = Factory_shared(); *sptr = 7; printf("%d\n", *sptr); //NG int* ptr = sptr.release(); //NG std::unique_ptr<int> sptr = std::move(sptr); }
int main() { job_happy(); job_unhappy(); }
get_deleterを使えばデリータを取り出すことができる。メモリを解放するかしないかを選択できるクラスのインスタンスをデリータにしておけば、その指示を出すことができるようになる。
#include <iostream> #include <memory>
// オブジェクトを削除しない場合を指定できるデリータ struct ReleasableDeleter { // 削除しない場合はtrue bool _release; ReleasableDeleter() :_release(false) {} template <class T> void operator()(T* p) { if (_release == false) { delete p; } } };
// テスト用のデータ型 class Object { public: const char* _name; Object(const char* name) :_name(name){} ~Object() { std::cout << "delete " << _name << std::endl; } };
int main() { Object* valid; { std::shared_ptr<Object> p1(new Object("obj1"), ReleasableDeleter()); std::shared_ptr<Object> p2(new Object("obj2"), ReleasableDeleter()); // 削除しない設定 ReleasableDeleter* deleter = std::get_deleter<ReleasableDeleter>(p1); deleter->_release = true; valid = p1.get(); } std::cout << "まだ生きている: " << valid->_name << std::endl; delete valid; //std::cout << "解放済み: " << valid->_name << std::endl; }
スレッドプールのコードの中でわからなかったcondition_variableについて調べた。
条件変数と訳される。wait()で待機状態にしたスレッドに対して、別のスレッドからnotify_all()で通知を送ると待機していたスレッドが動き出す。
#pragma warning(disable:4996) #include <iostream> #include <functional> #include <thread> #include <mutex> #include <condition_variable> std::mutex mtx; std::condition_variable cv;
void func1() { for (size_t i = 0; i < 10;i++) { _sleep(10); printf("func1 # %d\n", i); if (i == 5) { printf("*** func1 から通知を送る *** \n"); cv.notify_all(); } } }
void func2() { printf("待機開始\n"); std::unique_lock<std::mutex> ul(mtx); cv.wait(ul); printf("通知が来たので func2 スレッド再開\n"); for (size_t i = 0; i < 10; i++) { _sleep(10); printf("func2 : %d\n", i); } }
int main() { std::thread th1(func1); std::thread th2(func2); th1.join(); th2.join(); std::cout << "Hello World!\n"; }
condition_variableは、.wait()の場所で、notify_all()などで通知が来るまで待機するのだが、「たまに、なぜか知らないが、どういうわけか通知が来ていないのに待機が解除されてしまいスレッドが走り出す」という現象が起こるらしい。これをspurious wakeupといい、condition_variableを使う場合、この現象への対応をしておかなければならないという。
ステート変数notifyを追加し、この変数がtrueの時だけwaitを解除する。
#pragma warning(disable:4996) #include <iostream> #include <functional> #include <thread> #include <mutex> #include <condition_variable> bool notify = false; // Spurious Wakeup考慮 ステート変数 std::mutex mtx; std::condition_variable cv; void func1() { for (size_t i = 0; i < 10;i++) { _sleep(10); printf("func1 # %d\n", i); if (i == 5) { printf("*** func1 から通知を送る *** \n"); notify = true;// notify_allするときにしかnotifyがtrueになることはない cv.notify_all(); } } } void func2() { printf("待機開始\n"); std::unique_lock<std::mutex> ul(mtx); cv.wait(ul, [&]{return notify; });// notifyがtrueの時だけ待機解除 printf("通知が来たので func2 スレッド再開\n"); for (size_t i = 0; i < 10; i++) { _sleep(10); printf("func2 : %d\n", i); } } int main() { std::thread th1(func1); std::thread th2(func2); th1.join(); th2.join(); std::cout << "Hello World!\n"; }
以下のページにあったスレッドプールを使用してみる。
使い方はこんな感じ。Runnableを継承したクラスに処理を書き、ThreadPoolにaddする。
#pragma warning(disable:4996) #include <iostream> // https://cutlassfish.wordpress.com/2016/09/14/c-%E3%81%A7-worker-thread-%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3/ #include "WorkerThread.hpp"
class Work1 : public Runnable { const char* _name; const int _count; public: Work1(const char* name,int count): _name(name),_count(count) {} virtual void run() override { for (int i = 0; i < 10; i++) { _sleep(_count); printf("%s == %d\n",_name, i); } } };
int main() { int thread_count = 5;// 同時に走るスレッドの数 int queue_count = 5;// 積んでおける仕事の数 ThreadPool pool(thread_count,queue_count); pool.add(std::make_shared<Work1>("h",20)); pool.add(std::make_shared<Work1>("i",25)); pool.add(std::make_shared<Work1>("o",30)); pool.add(std::make_shared<Work1>("k",35)); _sleep(5000); std::cout << "End of main\n"; }
構造的には、threadCount個のスレッドが作られ、それらがキューを監視し、キューにジョブが入っていたらそれをとってきて実行する、という構造になっている。
スレッド数を指定できる。同時に実行できる処理の数はスレッドの数だけ。
例えば以下のように指定する。
int main() {
int thread_count = 2; int queue_count = 5;
ThreadPool pool(thread_count,queue_count); pool.add(std::make_shared<Work1>("h",20)); pool.add(std::make_shared<Work1>("i",25)); pool.add(std::make_shared<Work1>("o",30)); pool.add(std::make_shared<Work1>("k",35)); _sleep(5000); std::cout << "End of main\n"; }
一度に処理できるのは二つまでなので、並列に処理されるのは二つだけだが、最終的に四つすべての処理が行われている。

キューの最大個数を2にすると、登録できる処理が二つだけになる。たとえ4個の処理をaddに与えても、キューに入りきらないものは捨てられている。
int thread_count = 5; int queue_count = 2;