A thread on a form stops when running a function
- Hello
I have a form with a thread that is changing the location of a label once in a certain time. It works very good.
BUT, when I click on a button or interacts some other way with the form - for example, loading a picture, the thread stops and waits until the second operation finishes.
Any idea how I can let the thread that moves the label to work independently?
Thanks
Eli
答案
- The worker thread doesn't actually do any updating of the UI, it just changes the values that represent the location of the label and queue up some work to be done to update the UI (invalidate things, etc.).
The MAIN THREAD is still responsible for doing all that work. That queue belongs to the main thread, in other words, it is the main thread that fetches work from the queue and performs it.
If the main thread is busy loading a picture, it cannot update the UI with the new position of the label. Once the main thread is done doing work, the UI should update as it flushes its queue.
This is a good reason not to have the UI thread do any real lengthy work -- to keep the UI as responsive as possible.
Don't load pictures in your UI thread.- 已标记为答案Rudedog2版主2009年11月23日 14:39
全部回复
- Hi Eli... could you post some code or explain into further details.
If I understand correctly what you are doing, you won't be able to update your label if the main thread is busy doing something else. Ideally, this shouldn't be a problem because operations on the UI thread should take less time than noticeable.
Anyways, we won't be able to pin point the problem unless you post some code.
I always try to Keep it Sharp & simple. - Hi Fernano
Thanks for your reply.
I have a sample project that has the same symptoms. Since there isn't any way to load it here, I loaded it to RapidShare.
Link to RapidShare: http://rapidshare.com/files/298218345/Threads.zip.html
The picture is heavy on purpose in order to let the scrolling label to stop.
Thanks for your help
Eli - I'm sorry, Eli, I won't download your project and try it. Post some code and I'll try to help you.
Still, the main suggestion is the same: avoid lengthy operations on the main UI thread.
I always try to Keep it Sharp & simple. - Hello Fernano
Here are some lines from the code:
The scenario is
1. Start the scrolling text - The text is scrolling
2. Load the picture - The scrolling text is stopping and resuming after the loading => Problem
// Delegates:
delegate void LoadPicDelegate();
delegate void ScrollLabelDelegate();
// Scrolling label proc
private void ScrollLabel()
{
if (label1.InvokeRequired)
{
ScrollLabelDelegate lScrollLabel = new ScrollLabelDelegate(ScrollLabel);
BeginInvoke(lScrollLabel);
}
else
{
while (true)
{
Application.DoEvents();
label1.Left = label1.Left + 1;
if (label1.Left > this.Width)
label1.Left = 5 - label1.Width;
Thread.Sleep(15);
}
}
}
// Picture loading proc
private
void LoadPic()
{
string lFileName = Directory.GetCurrentDirectory() +"\\IMG_1046.jpg";
if (pictureBox1.InvokeRequired)
{
LoadPicDelegate lLoadPic = new LoadPicDelegate(LoadPic);
BeginInvoke(lLoadPic);
}
else
{
pictureBox1.Image = Image.FromFile(lFileName);
}
}
// Click on Button 1 and scroll the text
private void button1_Click(object sender, EventArgs e)
{
Thread ScrollThread = new Thread(new ThreadStart(ScrollLabel));
// Start the thread
ScrollThread.Start();
}
// Click on Button 2 and load the picture
private void button2_Click(object sender, EventArgs e)
{
Thread LoadPicThread = new Thread(new ThreadStart(LoadPic));
LoadPicThread.Priority = ThreadPriority.BelowNormal;
// Start the thread
LoadPicThread.Start();
}
Thanks for the effort ;-)
Eli - The worker thread doesn't actually do any updating of the UI, it just changes the values that represent the location of the label and queue up some work to be done to update the UI (invalidate things, etc.).
The MAIN THREAD is still responsible for doing all that work. That queue belongs to the main thread, in other words, it is the main thread that fetches work from the queue and performs it.
If the main thread is busy loading a picture, it cannot update the UI with the new position of the label. Once the main thread is done doing work, the UI should update as it flushes its queue.
This is a good reason not to have the UI thread do any real lengthy work -- to keep the UI as responsive as possible.
Don't load pictures in your UI thread.- 已标记为答案Rudedog2版主2009年11月23日 14:39
- Hi Wyck
Thanks for the comment.
Can you advise a way to have both jobs running without stopping?
Thanks
Eli - Use a BackgroundWorker to load the image and use its RunWorkerCompleted event handler to set the image on the PictureBox. The RunWorkerCompletedEventArgs has a property called Result where you'll put the loaded image.
- Take a look at this thread.
BackgroundWorker and UI control text for further processing.
It shows how to update a progress bar with a background worker.
It also shows how to dispose of workers, and start new ones dynamically.
Rude =8^D
Mark the best replies as answers. "Fooling computers since 1971." - Looks like you've got things a bit backwards. The InvokeRequired pattern is used to move things to the UI thread when it's in a different thread. You're creating a new thread that immediately calls the InvokeRequired, thus immediately putting the functionality back on the UI thread where it started.
I'd say first of all, kill the scroll thread completely and use a windows forms timer instead. Then your button1_Click should just be timer1.Start(). That will clean up your scroll code, and you can get rid of a Thread.Sleep that is now taking place on your UI thread.
Next of all, as I said, everything in the "else" part of the InvokeRequired pattern is executed in the UI thread. So since Image.FromFile could be a long-running process, a better thing to do would be to create a member variable "Image _image", and call "_image = Image.FromFile(fn)" in the "if" part, and call "pictureBox1.Image = _image" in the else part.
That should work for you, but ultimately there's no reason to even use the InvokeRequired pattern there anyway, since you know what thread each function is taking place on. It'd be better to separate into two functions, something like:
Image _image;
void LoadPic()
{
string lFileName = Directory .GetCurrentDirectory() +" \\IMG_1046.jpg " ;
_image = Image .FromFile(lFileName);
BeginInvoke( new LoadPicDelegate (DisplayPic) );
}
void DisplayPic()
{
pictureBox1.Image = _image;
} Hi Wyck
Start thinking like a manager. Delegate tasks instead of doing them yourself.
Thanks for the comment.
Can you advise a way to have both jobs running without stopping?
Thanks
Eli
Create a thread to load the image. Update the UI with a "loading" message. Then when the thread completes have it invoke work on the UI thread that updates the UI with a "complete" message.
Add a timer to your form and set the timer interval to be the interval at which you want the scroll to update and enable the timer. The timer will run in the context of the main thread, so just update the label's position in the timer's Tick handler.
here's an example. Create a form, add a label and a timer then do this:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load( object sender, EventArgs e ) { ThreadPool.QueueUserWorkItem( delegate { this.Invoke( new Action( delegate { label1.Text = "Loading"; timer1.Interval = 250; timer1.Enabled = true; timer1.Tick += new EventHandler( timer1_Tick ); } ) ); Thread.Sleep( 5000 );// Load Image Here this.Invoke( new Action( delegate { timer1.Enabled = false; this.Text = "Done"; } ) ); } ); } void timer1_Tick( object sender, EventArgs e ) { label1.Text = label1.Text + "."; } }
I'm just changing the label's text, but you can change the position or whatever.- Hello Wyck
I'm getting an error "Using the generic type 'System.Action<T>' requires '1' type arguments".
Any idea how to solve that?
Thanks
Eli - 2008?
- Yes 2008
- Post the code that elicits the error. The code I posted does not generate that error.
It says you need to provide a type argument. Are you sure you have all your brackets in the right place?
this.Invoke( new Action( delegate {} ) ); Take a look at this thread.
BackgroundWorker and UI control text for further processing.
It shows how to update a progress bar with a background worker.
It also shows how to dispose of workers, and start new ones dynamically.
Rude =8^D
Mark the best replies as answers. "Fooling computers since 1971."
hmmph, bad link. Here's the code for a *complete* form.
Add a CLASS file to a project, not a form.
// Form1.cs -- copy the code into a new class file, NOT a new form file.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace BGWDemo
{
public partial class Form1 : Form
{
BackgroundWorker worker;
private Button btnPause;
private bool workerPaused;
int workerData;
public Form1()
{
InitializeComponent();
}
private Button btnStart;
private Button btnStop;
private ProgressBar progressBar1;
private Label label1;
private Label label2;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
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.btnStart = new System.Windows.Forms.Button();
this.btnStop = new System.Windows.Forms.Button();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.btnPause = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// btnStart
//
this.btnStart.Location = new System.Drawing.Point(76, 94);
this.btnStart.Name = "btnStart";
this.btnStart.Size = new System.Drawing.Size(75, 23);
this.btnStart.TabIndex = 0;
this.btnStart.Text = "Start";
this.btnStart.UseVisualStyleBackColor = true;
this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
//
// btnStop
//
this.btnStop.Location = new System.Drawing.Point(239, 93);
this.btnStop.Name = "btnStop";
this.btnStop.Size = new System.Drawing.Size(75, 23);
this.btnStop.TabIndex = 1;
this.btnStop.Text = "Stop";
this.btnStop.UseVisualStyleBackColor = true;
this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
//
// progressBar1
//
this.progressBar1.Location = new System.Drawing.Point(76, 65);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(238, 23);
this.progressBar1.TabIndex = 2;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(174, 72);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(35, 13);
this.label1.TabIndex = 3;
this.label1.Text = "label1";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(96, 40);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(35, 13);
this.label2.TabIndex = 4;
this.label2.Text = "label2";
//
// btnPause
//
this.btnPause.Location = new System.Drawing.Point(158, 93);
this.btnPause.Name = "btnPause";
this.btnPause.Size = new System.Drawing.Size(75, 23);
this.btnPause.TabIndex = 5;
this.btnPause.Text = "Pause";
this.btnPause.UseVisualStyleBackColor = true;
this.btnPause.Click += new System.EventHandler(this.btnPause_Click);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(376, 196);
this.Controls.Add(this.btnPause);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.btnStop);
this.Controls.Add(this.btnStart);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private void Form1_Load(object sender, EventArgs e)
{
this.workerPaused = false;
//
this.btnStop.Enabled = false;
this.btnPause.Enabled = false;
//
this.label1.Text = "";
this.label1.Visible = false;
//
this.label2.Text = "No Progress To Report.";
//
this.progressBar1.Step = 1;
this.progressBar1.Maximum = 100;
this.progressBar1.Minimum = 0;
this.progressBar1.Style = ProgressBarStyle.Continuous;
//
InitializeBGW();
return;
}
private void InitializeBGW()
{
this.worker = null;
this.worker = new BackgroundWorker();
this.worker.WorkerReportsProgress = true;
this.worker.WorkerSupportsCancellation = true;
this.worker.DoWork +=
new DoWorkEventHandler(worker_DoWork);
this.worker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
this.worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
this.worker.Disposed += new EventHandler(worker_Disposed);
//
}
private void btnStart_Click(object sender, EventArgs e)
{
this.InitializeBGW();
this.label1.Visible = true;
this.btnStop.Enabled = true;
this.btnPause.Enabled = true;
this.btnStart.Enabled = false;
this.workerPaused = false;
this.worker.RunWorkerAsync(this.workerData);
}
private void btnStop_Click(object sender, EventArgs e)
{
this.btnStop.Enabled = false;
this.btnPause.Enabled = false;
this.btnStart.Enabled = true;
this.worker.CancelAsync();
this.workerData = 0;
this.progressBar1.Value = this.workerData;
}
private void btnPause_Click(object sender, EventArgs e)
{
this.workerPaused = true;
this.btnStop.Enabled = true;
this.btnPause.Enabled = false;
this.btnStart.Enabled = true;
this.worker.CancelAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
DoTheWork(e);
}
private void DoTheWork(DoWorkEventArgs e)
{
int startPosition = (int)e.Argument;
for (int i = startPosition; i < 100; i++)
{
System.Threading.Thread.Sleep(90);
worker.ReportProgress(i, "Background Worker Is In Progress ...");
if (worker.CancellationPending)
{
e.Cancel = true; // exiting the loop properly,
i = 100; // calling return here can throw future exceptions
}
}
return;
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.workerData = e.ProgressPercentage;
string progressReport = (string)e.UserState;
this.progressBar1.Value = this.workerData;
this.label1.Text = this.progressBar1.Value.ToString();
this.label2.Text = progressReport;
//
// fire an event in this class to notify other class
//
return;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
this.label2.Text = "An error was encountered.";
}
else
{
if (e.Cancelled)
{
if (this.workerPaused)
{
this.label2.Text = "The work was paused by the user.";
this.workerPaused = false;
}
else
{
this.label2.Text = "The work was cancelled by the user.";
this.workerData = 0;
this.progressBar1.Value = this.workerData;
}
}
else
{
this.label1.Visible = false;
this.progressBar1.Value = this.progressBar1.Minimum;
this.label2.Text = "Work has been completed.";
this.workerData = 0;
this.btnStop.Enabled = false;
this.btnPause.Enabled = false;
this.btnStart.Enabled = true;
}
}
this.worker.Dispose(); // Dispose of the object, do not re-use.
return;
}
void worker_Disposed(object sender, EventArgs e)
{
//if (worker == null)
//{
// Debug.WriteLine("Worker has not been disposed");
//}
//else
//{
// Debug.WriteLine("Worker has been disposed");
//}
return;
}
}
}
Mark the best replies as answers. "Fooling computers since 1971."

