tags:

views:

177

answers:

3

These codes generates us this error:

Cross-thread operation not valid: Control 'progressBar2' accessed from a thread other than the thread it was created on.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

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

        ThreadStart ts1;
        ThreadStart ts2;
        Thread t1;
        Thread t2;

        private void btnStart_Click(object sender, EventArgs e)
        {
            ts1 = new ThreadStart(z1);
            ts2 = new ThreadStart(z2);

            t1 = new Thread(ts1);
            t2 = new Thread(ts2);

            t1.Start(); 
            t2.Start();

            btnStart.Enabled = false;
        }

        public void z1()
        {

            for (int i = 1; i < 60; ++i)
            {
                progressBar1.Value += 1;
                for (int j = 1; j < 10000000; ++j)
                {
                    j += 1;
                }
            }
        }

        public void z2()
        {
            for (int k = 1; k < 100; ++k)
            {
                progressBar2.Value += 1;
                for (int j = 1; j < 25000000; ++j)
                {
                    j += 1;
                }
            }
        }

        private void btnstop_Click(object sender, EventArgs e)
        {
            t1.Suspend(); 
            t2.Suspend();
        }

        private void btnContinue_Click(object sender, EventArgs e)
        {
            t1.Resume(); 
            t2.Resume();

        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            if (t1.IsAlive)
            {
                MessageBox.Show("Çalışan threadler var program sonlanamaz.");
            }
            else
            {
                this.Close();
            } 

        }
    }
}
+7  A: 

You can't access a UI control in a thread other than the UI thread responsible for that control.

See the WinForms page in my threading tutorial and also search for tutorials on BackgroundWorker, a component introduced in .NET 2.0 which makes life much easier (particularly for progress bars).

Additionally, I'd try to avoid using Thread.Suspend/Resume, preferring a more co-operative approach (e.g. with Monitor.Wait/Pulse). That allows you to avoid suspending a thread while it holds a lock, etc.

Jon Skeet
Jon, wouldn't my solution work for this scenario?
Rashmi Pandit
Background worker is definetly what you want. It even has a report progress event.
Alex
Rashmi: That ends up doing a lot of work *in the UI thread* which should also be avoided. You only want to do the UI work in the UI thread, not the looping to ten million.
Jon Skeet
Oh ... ok .. I get your point. In my code (where I am using a similar approach), for populating a combo box from db, would it be ok if I call the datalayer method before checking InvokeRequired and only the actual combo population code inside if (this.InvokeRequired) { this.Invoke((MethodInvoker)delegate { only populate combo code; }); }I hope I havent got you confused :D
Rashmi Pandit
Well, that would be okay in terms of threading - but it suggests there isn't enough separation between a method which fetches data and a method which changes the display. Also you don't need an anonymous method - just use a method group conversion: (MethodInvoker) z1Safe
Jon Skeet
Ok .... thanks Jon. Time for refactoring
Rashmi Pandit
+1  A: 

For both z1 and z2 do this: Add a zSafe() Call zSafe() within z() after InvokeRequired check.

public void z1Safe()
        {

            for (int i = 1; i < 60; ++i)
            {
                progressBar1.Value += 1;
                for (int j = 1; j < 10000000; ++j)
                {
                    j += 1;
                }
            }
        }

        public void z1()
        {

            if (this.InvokeRequired)
            {
                this.Invoke((MethodInvoker)delegate { z1Safe(); });
            }
            else
                z1Safe();
        }

I just implemented a similar solution in my windows form for asyn del call and it works fine.

Rashmi Pandit
A: 

Cross thread exceptions are very common when you are working on a multithread application and it happens when you try to invoke a member of a control in a thread other than its own thread.To avoid that you can check InvokeRequired property of that control and invoke a delegate in its own thread or use a BackgroundWorker to run a process in background (another thread) and handle its events,When you handle BackgroundWorker events ,event handler methods will run on the main thread so there's no need to invoke a delegate.

Here you can find some information about BackgroundWorker

Beatles1692