Visual C# Developer Center > Visual C# Forums > Visual C# General > A thread on a form stops when running a function
Ask a questionAsk a question
 

AnswerA thread on a form stops when running a function

  • Monday, October 26, 2009 1:56 PMAfikim Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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

Answers

  • Monday, October 26, 2009 6:17 PMWyck Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    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.

All Replies

  • Monday, October 26, 2009 2:09 PMFernando Nicolet Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Monday, October 26, 2009 4:38 PMAfikim Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Monday, October 26, 2009 4:57 PMFernando Nicolet Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Monday, October 26, 2009 6:09 PMAfikim Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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

  • Monday, October 26, 2009 6:17 PMWyck Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    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.
  • Monday, October 26, 2009 6:56 PMAfikim Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi Wyck

    Thanks for the comment. 

    Can you advise a way to have both jobs running without stopping?

    Thanks

    Eli
  • Monday, October 26, 2009 7:21 PMtgrt Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Monday, October 26, 2009 7:23 PMRudedog2ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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."
  • Monday, October 26, 2009 7:28 PMDax Fohl Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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;

    }
  • Tuesday, October 27, 2009 12:27 PMWyck Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    Hi Wyck

    Thanks for the comment. 

    Can you advise a way to have both jobs running without stopping?

    Thanks

    Eli
    Start thinking like a manager.  Delegate tasks instead of doing them yourself. 

    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.
  • Wednesday, November 04, 2009 2:32 PMAfikim Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Thursday, November 05, 2009 2:53 AMWyck Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    2008?
  • Thursday, November 05, 2009 6:15 AMAfikim Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Yes 2008
  • Friday, November 06, 2009 7:06 PMWyck Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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 {} ) );
  • Friday, November 06, 2009 7:54 PMRudedog2ModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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."