0

This is how the demo windows form app looks like:

enter image description here

And this is the implemented code:

public partial class HornMorphoWindow : Form
{
    private static dynamic _morpho;

    private static string _analyzeWord;

    private static Logger logger = LogManager.GetCurrentClassLogger();

    public delegate void StatusDelegate();

    public HornMorphoWindow()
    {
        InitializeComponent();
    }

    private void HornMorphoWindow_Load(object sender, EventArgs e)
    {
        var splashScreen = new Splash();  // This is Splash Form
        splashScreen.Show();

        Application.DoEvents(); // Force the splash screen to be shown

        Task.Factory.StartNew(LoadLibrary).Wait(); // Wait for the library to load

        splashScreen.Close();
    }

   // When a button clicked
   private void analyze_Click(object sender, EventArgs e)
   {
        if (string.IsNullOrEmpty(amharicInput.Text)) return;

        _analyzeWord = amharicInput.Text; // amharicInput is TextBox
        analyze.Enabled = false; // analyze is a button

        Task.Factory.StartNew(AnalyzeWord);
    }

    private static void LoadLibrary()
    {
        logger.Info("Loaidng Library.....");
        using (Py.GIL())
        {
            _morpho = Py.Import("l3");
            _morpho.load_lang("am");
        }
        logger.Info("Library Loaded Sucessfully!");
    }

    private void AnalyzeWord()
    {
        logger.Info("Word Analyzation Started. Word: " + _analyzeWord);

        using (Py.GIL())
        {
            _analyzeWord = _morpho.anal_word("am", _analyzeWord, Py.kw("nbest", 1));
        }

        logger.Info("Word Analyzation Ended. Result:\n " + _analyzeWord);

        try
        {
            this.Invoke(new StatusDelegate(UpdateStatus));
        }
        catch
        {
           // Some problem occurred
        }

     }

    private void copyButton_Click(object sender, EventArgs e)
    {
        if (string.IsNullOrEmpty(result.Text)) return;

        Clipboard.SetText(result.Text, TextDataFormat.UnicodeText);
    }


    private void UpdateStatus()
    {
        result.Text = _analyzeWord; // result is a label

        copyButton.Visible = true; // copyButton shows up when result is successfull

        analyze.Enabled = true;
    }
}

I asked this question because, I call C# function which use using(Py.GIL()) block and is executed with a new thread as shown on the above code. It works for the first round(sometimes it does not), and for the next round, it stops on the using block and the application stays the same with out showing any result or exception.

The application works if I removed the using(Py.GIL()) block, and do other stuff for the sake of testing. For example, Change the result label text to something else.

What am I doing wrong?

UPDATED

The problem is not on LoadLibrary function. It is on AnalyzeWord function. On LoadLibrary it successfully executes it but not on AnalyzeWord function.

ash
  • 2,902
  • 3
  • 19
  • 34
  • 3
    Seems very similar to the Github issue [using(Py.Gil()) hangs while calling from other than init thread](https://github.com/pythonnet/pythonnet/issues/109) – Damien_The_Unbeliever Mar 13 '17 at 09:21
  • 2
    Why would you do `Task.Factory.StartNew(LoadLibrary).Wait()`, you might as well just run the sync code. Plus, events in C# can be made `async` so you would just do `private async void HornMorphoWindow_Load()` and `await Task.Run(LoadLibrary)` – Callum Linington Mar 13 '17 at 09:21
  • 3
    The Wait() call in the Load event handler is pretty lethal, it causes the Invoke() call to deadlock. Why you still see the form is not so clear, some kind of re-entrancy through Python perhaps. That it next dies on the Python's global interpreter lock is certainly not unexpected, standard re-entrancy problem and should be visible in the debugger's Call Stack window. You will have to re-think this. – Hans Passant Mar 13 '17 at 09:44
  • @Damien_The_Unbeliever I followed your suggestion and added the following lines to HornMorphWindow_Load function `PythonEngine.Initialize(); PythonEngine.BeginAllowThreads();` and it worked. And @Callum I'll re-implement as you suggested and Thanks. – ash Mar 13 '17 at 19:21

1 Answers1

0

I followed the suggestion by @Damien and came up with this solution and it worked.

private void HornMorphoWindow_Load(object sender, EventArgs e)
{
    var splashScreen = new Splash();  // This is Splash Form
    splashScreen.Show();

    Application.DoEvents(); // Force the splash screen to be shown

    // The newly added line and the solution for the problem
    PythonEngine.Initialize();
    PythonEngine.BeginAllowThreads();
    //********************************

    Task.Factory.StartNew(LoadLibrary).Wait(); // Wait for the library to load

    splashScreen.Close();
}

PythonEngine.BeginAllowThreads() must be initialized on the Main thread or else it does not work.

The main thread will hold the GIL after initialization until you explicitly release it by calling PythonEngine.BeginAllowThreads() from the main thread (not from your background thread)

ash
  • 2,902
  • 3
  • 19
  • 34
  • There is no reason to do `Task.Factory.StartNew(LoadLibrary).Wait()`, that is just a more recsource intensive version of just calling `LoadLibrary()` directly. There is 0 reason to create that thread here. – Scott Chamberlain Mar 14 '17 at 13:55