0

My desktop app uses IronPython to perform various scripting functions, but we're under pressure to support Numpy (which isn't possible with IP), so I'm currently looking into Python.Net instead, but I'm a bit unclear on how to manage the lifetimes of objects such as the GIL. Python.Net C# examples typically look like this:

using (Py.Gil())
{
    using (var scope = Py.CreateScope())
    {
        // Do everything in here
    }
}

I have written a class that encapsulates running of Python code, and broadly looks like this (error handling etc. removed for brevity):

public class ScriptRunner()
{
    private readonly PyModule _scope;
    private readonly Py.GILState _gil;
    private PyObject _compiledScript;

    public ScriptRunner()
    {
        _gil = Py.GIL();
        _scope = Py.CreateScope();
    }

    public Dispose()
    {
        _scope?.Dispose();
        _gil?.Dispose();
    }

    public void SetVariable(string name, object value)
    {
        _scope.Set(name, value);
    }

    public void PrepareScript(string pythonCode)
    {
        _compiledScript = PythonEngine.Compile(pythonCode);
    }

    public void ExecuteScript()
    {
        _scope.Execute(_compiledScript);
    }
}

It's common for code in my application to execute the same Python code multiple times, but with different variable values, so typical usage of the above class will look something like this:

_scriptRunner.PrepareScript(myPythonCode);

_scriptRunner.SetVariable("x", 123);
_scriptRunner.ExecuteScript();

_scriptRunner.SetVariable("x", 456);
_scriptRunner.ExecuteScript();    

...

There will be numerous instances of this class throughout my app, used wherever there is a need to execute Python code. My main concern is the "GIL" lifecycle aspect, which I don't really understand. Will the above design mean that only one instance will actually be able to work (the one with the "handle" to the GIL)?

Rather than managing the GIL (and scope) object lifetimes in the constructor and Dispose(), I was wondering whether I should instead add explicit methods to create and free these objects, e.g.:

public void Initialise()
{
    _gil = Py.GIL();
    _scope = Py.CreateScope();
}

public Free()
{
    _scope?.Dispose();
    _gil?.Dispose();
}

The usage would then end up more like this, where the GIL is only "locked" (if that's the right word) for the duration of the work:

_scriptRunner.Initialise();

_scriptRunner.PrepareScript(myPythonCode);

_scriptRunner.SetVariable("x", 123);
_scriptRunner.RunScript();

_scriptRunner.SetVariable("x", 456);
_scriptRunner.RunScript();  

...

_scriptRunner.Free();

Thoughts?

Andrew Stephens
  • 9,413
  • 6
  • 76
  • 152

1 Answers1

1

Only one instance of GIL can exist and be active at a time. using GIL behaves similar to C# lock statement. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/lock

LOST
  • 2,956
  • 3
  • 25
  • 40