update gui from worker thread problem

B

Buzz

I want to have a worker thread that continually checks a particular
state. When the state changes, the worker thread Invoke's the gui to
update a label. I also have a button that causes the thread to stop
and start, using a flag that the worker checks every iteration.
This works fine if I hit my button casually. But if I hit it like a
madman (2+ times per second) my app hangs. I break the debugger and
find I'm stuck in my StopMyThread() routine, waiting for the worker to
stop... but it already has! (Or has it? It is in the debugger's
thread window, but I add a breakpoint inside MyThreadProc() and it
won't get hit) It looks like I have a synchronization problem... but
I don't see it.

Below is a minimalistic sample app that demonstrates the problem... at
least on my Jornada 540 PocketPC 2000. Any ideas?


using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Data;
using System.Threading;

namespace ThreadTest
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button button1;

private bool myFlag = false;
private bool myThreadEnabled = false;
private bool myThreadRunning = false;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.mainMenu1 = new System.Windows.Forms.MainMenu();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
//
// label1
//
this.label1.Location = new System.Drawing.Point(64, 32);
this.label1.Size = new System.Drawing.Size(120, 20);
this.label1.Text = "label1";
//
// label2
//
this.label2.Location = new System.Drawing.Point(64, 64);
this.label2.Text = "label2";
//
// button1
//
this.button1.Location = new System.Drawing.Point(72, 120);
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form1
//
this.Controls.Add(this.button1);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Menu = this.mainMenu1;
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>

static void Main()
{
Application.Run(new Form1());
}

private void Form1_Load(object sender, System.EventArgs e)
{
label1.Text = myFlag.ToString();
StartMyThread();
}

private void button1_Click(object sender, System.EventArgs e)
{
StopMyThread();
StartMyThread();
}

private void StartMyThread()
{
ThreadStart ts = new ThreadStart(MyThreadProc);
Thread worker = new Thread(ts);

Monitor.Enter(this);
myThreadEnabled = true;
myThreadRunning = true;
Monitor.Exit(this);

this.label2.Text = "Running";
this.label2.Update();
worker.Start();
}

private void StopMyThread()
{
bool stillRunning = true;

Monitor.Enter(this);
myThreadEnabled = false;
Monitor.Exit(this);

while(stillRunning)
{
Monitor.Enter(this);
stillRunning = myThreadRunning;
Monitor.Exit(this);
}

this.label2.Text = "Stopped";
this.label2.Update();
}

private void UpdateLabel1(object sender, EventArgs e)
{
Monitor.Enter(this);
this.label1.Text = myFlag.ToString();
Monitor.Exit(this);
this.label1.Update();
}

private void MyThreadProc()
{
bool keepRunning;

Monitor.Enter(this);
myThreadRunning = true;
keepRunning = myThreadEnabled;
Monitor.Exit(this);

do
{
Monitor.Enter(this);
myFlag = !myFlag;
Monitor.Exit(this);

this.Invoke(new EventHandler(this.UpdateLabel1));

Thread.Sleep(100);

Monitor.Enter(this);
keepRunning = myThreadEnabled;
Monitor.Exit(this);

}while(keepRunning);

Monitor.Enter(this);
myThreadRunning = false;
Monitor.Exit(this);
}
}
}


Thanks for any help!
Buzz
 
C

Chris Tacke, eMVP

I think the problem is that you're starting a whole new thread every time,
yet you have a single global for whether or not they should all run or die.
Is this your intent? Maybe I'mmissing something, but why not suspend and
resume a single thread?

-Chris
 
B

buzzweetman

Chris,
I appreciate the response.

I will try implementing your suggestion. And it will be more efficient (though probably imperceptible in my app).

But I still don't see what is causing my problem. Since I wait for MyThreadProc() to stop before I start a new one, I don't see how having a single global myThreadRunning and myThreadEnable would matter. Only one thread is running at a time (though technically the previous one(s) may be still returning while I'm creating the next one)

Any more explainations on why there is a problem here?
Buzz

**********************************************************************
Sent via Fuzzy Software @ http://www.fuzzysoftware.com/
Comprehensive, categorised, searchable collection of links to ASP & ASP.NET resources...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top