1

I'm running some scripts in runtime, but it's freezing my UI, I'm calling the CodeProvider inside a Thread, but it still freezing.

In my form I call:

var mre = new ManualResetEvent(false);
Thread tr = new Thread(() =>
    {
         Script sp = new Script();
         code = textBox.Text;
         sp.Comp(code);
         mre.Set();
    });
tr.Start();
mre.WaitOne();

I'm using the mre.WaitOne() because I want to wait the thread finish to keep running my code.

Tried to use the same way inside the Compile method too:

public bool Comps(string code)
{
    var mre = new ManualResetEvent(false);
    Thread tr = new Thread(() =>
    {
        //Code to generate a CompilerResult and generate the assembly
        Run();
        mre.Set();
    });
    tr.Start();
    mre.WaitOne();
    return true;
}

But while it's waiting it still freezing the UI.

Any ideas?

Thanks

DaveShaw
  • 52,123
  • 16
  • 112
  • 141
Kyore
  • 388
  • 2
  • 7
  • 29
  • If your app is WPF based, [this](http://msdn.microsoft.com/en-us/magazine/cc163328.aspx) should be relevant. – sovanesyan Mar 01 '12 at 21:56
  • 1
    Your `mre.Set();` is at the very end of the Thread. That means `mre.WaitOne();` is the same as `tr.Join();` which means the whole thing is essentially single-threaded. – H H Mar 01 '12 at 22:12

3 Answers3

4

I'm using the mre.WaitOne() because I want to wait the thread finish to keep running my code.

What did you expect to happen if you force the calling thread to freeze until your processing thread has completed processing? Doing it this way, there is no point in having that extra thread and if the calling thread is the UI thread, of course it will freeze.

If you do background processing you cannot wait for the result synchronously, instead you have to notify the UI in some sort of fashion that the processing is done, i.e. using a callback or dispatching the result back to the UI in some other form.

BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
2

The entire point of multi-threading is to allow the Thread to execute on it's own, independent of any other threads. What you want to do is use a callback to signal the completion of your thread and then have your UI respond to the completion.

The BackgroundWorker class has an event already built in for this purpose.

There are three events you want to subscribe to:

bw.DoWork += 
    new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += 
    new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += 
    new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

DoWork is where your work will happen. ProgressChanged allows you to update the UI of progress. RunWorkerCompleted will pop the event with your DoWork function has completed.

This object handles the threading and can be set to run asynchronously by running the bw.RunWorkerAsync() call.

See the following page for detail for this:

http://msdn.microsoft.com/en-us/library/cc221403%28v=vs.95%29.aspx

As an example:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show(String.Format("UI thread: {0}", Thread.CurrentThread.ManagedThreadId));
        this.Invoke(new MethodInvoker(delegate() { MessageBox.Show(String.Format("Invoke thread: {0}", Thread.CurrentThread.ManagedThreadId)); }));

        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        MessageBox.Show(String.Format("Worker thread: {0}", Thread.CurrentThread.ManagedThreadId));
    }
}

This example can be built by adding one button and one background worker to a form. Wire up the events through the events designer for the button1_Click and the backgroundWorker1_DoWork function. You should have three MessagesBoxes that pop up after clicking button1. You'll notice the Id for the UI thread and the Invoke thread are the same, which means that any processing you do from the invoke will cause your UI thread to wait. The third popup is from the worker thread, which has a different ID.

Walk
  • 1,136
  • 8
  • 8
  • Thank you, I just found how to work with it and it`s working good for now. – Kyore Mar 02 '12 at 01:51
  • I tried to use a backgroundwork, and I tought that it solved my problem, but when I saw it was returning a cross thread exception, and if I use: this.Invoke(new MethodInvoker(delegate() { sp.Comp(code); })); It freezes the UI too. – Kyore Mar 03 '12 at 03:31
  • Seems like your processing is still happening on the main UI thread, I've add an example to the end of my answer to show you the behavior. – Walk Mar 05 '12 at 15:45
  • The backgroundowork is not freezing my UI(Form), but it returns the cross-thread exception while acessing the functions in the script, would be possible to avoid this exception inside the backgroundworker? – Kyore Mar 08 '12 at 03:38
  • Yes, are you trying to modify the UI from the functions in your script, that will definitely cause cross-thread exceptions. You'd want to store the state you want in an object and perform the updates on the `RunWorkerCompleted` function so that it's thread safe, or try using the `InvokeRequired` functions. See http://stackoverflow.com/questions/527947/invoke-required for more information on that. – Walk Mar 13 '12 at 15:06
1

Use BeginInvoke when done. For example:

delegate void MyAction();

void Form1_Load( object sender, EventArgs e )
{
    Thread tr = new Thread( () =>
    {
        Script sp = new Script();
        code = textBox.Text;
        sp.Comp(code);

        BeginInvoke( new MyAction( ThreadOperationEnded ) );
    } );
    tr.Start();
}

void ThreadOperationEnded()
{
    MessageBox.Show( "Finished!" );
}
AVIDeveloper
  • 2,954
  • 1
  • 25
  • 32