docstr: When task instances are created at runtime, C++ concurrently creates
  multiple instances of operators in C++, and concurrently destroys them
  once the task is finished. This means that the destructor of PythonOp
  will be called concurrently, so the GIL must be acquired. This
  test exercises this condition.
docstr_tok: ["When","task","instances","are","created","at","runtime",",","C++","concurrently","creates","multiple","instances","of","operators","in","C++",",","and","concurrently","destroys","them","once","the","task","is","finished",".","This","means","that","the","destructor","of","PythonOp","will","be","called","concurrently",",","so","the","GIL","must","be","acquired",".","This","test","exercises","this","condition","."]
code: def test_multi_instance_python_op(self):
      """
      When task instances are created at runtime, C++ concurrently creates
      multiple instances of operators in C++, and concurrently destroys them
      once the task is finished. This means that the destructor of PythonOp
      will be called concurrently, so the GIL must be acquired. This
      test exercises this condition.
      """
      with Task(num_instances=64) as task:
          with ops.loop(4):
              ops.Python((python_op_builder, [], {}))([], [])
      with LocalSession() as session:
          PythonOpStats.num_instances = 0
          PythonOpStats.num_calls = 0
          session.run(task)
          self.assertEqual(PythonOpStats.num_instances, 64)
          self.assertEqual(PythonOpStats.num_calls, 256)
  
imports: from caffe2.python import workspace
  from caffe2.python.core import Plan, to_execution_step, Net
  from caffe2.python.task import Task, TaskGroup, final_output
  from caffe2.python.net_builder import ops, NetBuilder
  from caffe2.python.session import LocalSession
  import unittest
  import threading
label_desc: Use image node_id 7 for calling the TestNetBuilder obj's underlying member method code with example usage: obj.test_multi_instance_python_op() without return types