i copied the following example Microsoft Thread Example
Which gives the code below
but i get an error on the line "this.progressBar1.Value = newval;" stating that "Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on."
what could be the issue?
thanks
damo
C# Code
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Thread trd = new Thread(new ThreadStart(this.ThreadTask));
trd.IsBackground = true;
trd.Start();
}
private void ThreadTask()
{
int stp;
int newval;
Random rnd = new Random();
while (true)
{
stp = this.progressBar1.Step * rnd.Next(-1, 2);
newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
Thread.Sleep(100);
}
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("This is the main thread");
}
}
First of all I strongly suggested to use some higher level techniques like Tasks rather than using Thread class directly. Task classes not only easier to use, they're more effective, easier to compose and easier to avoid such issues that you faced recently.
The main issue with your code that you trying to update UI objects from non-UI threads. UI technologies (like Windows Forms or WPF) requires that only thread that creates an UI object will access to their properties.
To fix this you should marshal control from non-UI thread to UI thread. And there is a plenty of options to do this (but all of them only a syntactic sugar around concept called SynchronizationContext):
Use synchronization context directly:
.
// storing SynchronizationContext in the private field of your form
private SynchronizationContext _syncContext = SyncrhonizationContext.Current;
private void MethodFromTheSeparateThread()
{
// Marshaling control to UI thread
_syncContext.Post(d =>
{
// Put all your code that access UI elements here
}, null);
}
Use InvokeRequired/Invoke as Gregor mentioned already
Use TaskScheduler.FromSynchronizationContext
.
private void ImplementLongRunningOperation()
{
int id;
string name;
Task.Factory.StartNew(() =>
{
// our long-runing part. Getting id and name
id = 42;
name = "Jonh Doe";
}).ContinueWith(t =>
{
// Handling results from the previous task.
// This callback would be called in UI thread!
label1.Text = id.ToString();
label2.Text = name;
}, TaskScheduler.FromSynchronizationContext);
}
As I mentioned, last approach (using Tasks) is a preferable way if you're working on .NET 4.0+. This not only saves you from some low-level classes but also lead to more clear design because you can clearly separate separate steps like getting the data and processing them.
You have to invoke new delegate:
delegate void ThreadTaskDelegate();
private void ThreadTask()
{
if (this.InvokeRequired)
{
ThreadTaskDelegate del = new ThreadTaskDelegate(ThreadTask);
this.Invoke(del, null);
}
else
{
int stp;
int newval;
Random rnd = new Random();
while (true)
{
stp = this.progressBar1.Step * rnd.Next(-1, 2);
newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
Thread.Sleep(100);
}
}
}
Happy coding! :)
The example is a poor one. You must access controls in the thread in which they are created. This is almost always the main UI thread. (It is possible to have separate UI threads for different forms each with their own message pumps. But don't worry about that right now.)
The background thread must use Control.Invoke(Delegate) to change to the main UI thread before accessing Controls. Then, when the UI work is done, get out of the UI thread as soon as possible.
For example:
private void ThreadTask()
{
// This code runs in the background thread.
while (true)
{
if (this.InvokeRequired)
{
// In order to access the UI controls, we must Invoke back to the UI thread
this.Invoke(new ThreadStart(SetRandomProgress));
}
else
{
// We are already in the UI thread, so we don't have to Invoke
SetRandomProgress();
}
// Wait briefly. This wait happens in the background thread.
// During this time, the UI is still responsive, because it is not blocked.
// You can verify this by tweaking the duration to something longer (say, 5000 ms).
Thread.Sleep(100);
}
}
private void SetRandomProgress()
{
Random rnd = new Random();
int stp = this.progressBar1.Step * rnd.Next(-1, 2);
int newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
}
You could rewrite your code like this, your progressBar will be updated in the UI thread, Invoke a method that does access to the progressBar through delegate. Check the code:
private void Form1_Load(object sender, EventArgs e)
{
Thread trd = new Thread(new ThreadStart(this.ThreadTask));
trd.IsBackground = true;
trd.Start();
}
private void ThreadTask()
{
Random rnd = new Random();
while (true)
{
int randValue = rnd.Next(-1, 2);
progressBar1.Invoke(new updater(UpdateProgressBar), new object[] {randValue});
Thread.Sleep(100);
}
}
private delegate void updater(int value);
private void UpdateProgressBar(int randValue)
{
int stp = this.progressBar1.Step * randValue;
int newval = this.progressBar1.Value + stp;
if (newval > this.progressBar1.Maximum)
newval = this.progressBar1.Maximum;
else if (newval < this.progressBar1.Minimum)
newval = this.progressBar1.Minimum;
this.progressBar1.Value = newval;
}
Related
It is know that Invoke method is used when u need to update gui from other thread. But How can I implement this without binding control to code?
Here's my test class:
class test
{
public List<Thread> threads = new List<Thread>();
public int nThreads = 0;
public int maxThreads = 5;
public void DoWork(object data)
{
string message = (string)data;
//MessageBox.Show(message);
}
public void CreateThread(object data)
{
if (nThreads >= maxThreads)
return;
Thread newThread = new Thread(DoWork);
threads.Add(newThread);
newThread.IsBackground = true;
newThread.Start(data);
nThreads++;
}
public void WindUpThreads()
{
//MessageBox.Show("count: " + nThreads.ToString());
for(int i = 0; i < threads.Count; i++)
{
if (threads[i].IsAlive == false)
{
threads[i].Abort();
threads.RemoveAt(i);
//MessageBox.Show("removing at " + i.ToString());
}
}
nThreads = threads.Count;
}
}
The question is = what tecnique I must use in order to update gui but not hardcode control into class? I've tried to pass delegate to DoWork Method, but this doesn't work (http://pastebin.com/VaSYFxPw). Thanks!
I'm using WinForms, .NET 3.5
Here's the button_click handler:
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
test thTest = new test();
string[] strings;
try
{
strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
bool flag = true;
int counter = 0;
int dataCount = strings.Length;
while (flag == true)
{
if (counter >= dataCount)
{
flag = false;
}
while (thTest.nThreads < thTest.maxThreads)
{
if (flag == false)
break;
thTest.CreateThread(strings[counter]);
//Data d = new Data();
//d.deleg = AddItem;
//d.mess = strings[counter];
//thTest.CreateThread((object)d);
//MessageBox.Show(counter.ToString());
counter++;
}
thTest.WindUpThreads();
if (flag == false)
{
do
{
thTest.WindUpThreads();
} while (thTest.nThreads != 0);
}
}
listBox1.Items.Add("Done");
}
The idea is that I'am launching threads for each task I want to process. After while I'am checking are there completed tasks, then they being shutdowned and new ones are launched until there no more tasks left.
Rather than making DoWork responsible for updating the UI with the results of the operation it performs, simply have it return the value:
//TODO change the type of the result as appropriate
public string DoWork(string message)
{
string output = "output";
//TODO do some work to come up with the result;
return output;
}
Then use Task.Run to create a Task that represents that work being done in a thread pool thread. You can then await that task from your button click handler.
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
test thTest = new test();
//I'd note that you really should pull out reading in this file from your UI code;
//it should be in a separate method, and it should also be reading
//the file asynchronously.
string[] strings;
try
{
strings = System.IO.File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
foreach (var line in strings)
{
var result = await thTest.DoWork(line);
listBox1.Items.Add(result);
}
listBox1.Items.Add("Done");
}
If you really want to be old school about it, you can use a BackgroundWorker instead. Simply do your work in the DoWork handler, setting the result (through the argument) when you've computed it, and update the UI with the result in the RunWorkerCompleted event handler. This lets you keep the UI and non-UI work separate, although it's far less powerful, general purpose, and extensible, as the newer features.
The question is = what tecnique I must use in order to update gui but not hardcode control into class? I've tried to pass delegate to DoWork Method, but this doesn't work
This is indeed the one of the possible techniques. It doesn't work because you have a blocking loop in the UI thread - the most of the code inside the button1_Click handler. It doesn't matter that you spawn additional worker threads - that code keeps the UI thread busy, thus Control.Invoke / Control.BeginInvoke doesn't work because they are processed by the UI thread message loop, which in this case has no chance to do that. The end result is a classical deadlock.
So, you can use the delegate approach, but to make it work, you need to move that code in a separate thread. Something like this
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
var worker = new Thread(DoWork);
worker.IsBackground = true;
worker.Start();
}
private void OnWorkComplete(Exception error)
{
if (error != null)
MessageBox.Show(error.Message);
button1.Enabled = true;
}
private void DoWork()
{
Exception error = null;
try { DoWorkCore(); }
catch (Exception ex) { error = ex; }
Invoke(new Action(OnWorkComplete), error);
}
private void DoWorkCore()
{
test thTest = new test();
// NOTE: No try/catch for showing message boxes, this is running on a non UI thread
string[] strings = File.ReadAllLines("C:\\users\\alex\\desktop\\test.txt");
bool flag = true;
int counter = 0;
int dataCount = strings.Length;
// The rest of the code...
// Pass a delegate to the other threads.
// Make sure using Invoke when you need to access/update UI elements
}
I'm trying to workout how to call chart1.Series[0].Points.AddXY(1, 4); from another thread.
I have been trying to adapt the examples which all show setting a text property using a delegate but I can't get them to work with the chart control.
could someone please help me?
delegate void SetTextCallback(string text);//assume text is the value and not the Text property
private void chartRefresh()
{
while (true)
{
//code to refresh chart
for (int i = 0; i < 30; i++)
{
if (this.chart1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.chart1.Series[0].Points.AddXY(i, i + 2);
}
chart1.Series[0].Points.AddXY(i, i + 2);
Thread.Sleep(500);
}
}
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.chart1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.chart1.Series[0].Points.AddXY(1, 4);
}
}
while (true)
{
for (int i = 0; i < 20000; i++)
{
Temp ++;
chart1 .Invoke((MethodInvoker)delegate
{
// Running on the UI thread
Series.Points.AddXY(i, random.Next(0,100));
});
}
You may find interesting the modern approach, using the Progress class. You can instantiate such an object in the UI thread, supplying the lambda function that will update the chart:
private IProgress<int> _chartProgress
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_chartProgress = new Progress<int>(i =>
{
this.chart1.Series[0].Points.AddXY(i, i + 2);
});
}
Then pass this object somehow to the background worker, and invoke its Report method any time you want the chart to be updated.
private void ChartRefresh(IProgress<int> progress)
{
while (true)
{
//code to refresh chart
for (int i = 0; i < 30; i++)
{
progress.Report(i);
Thread.Sleep(500);
}
}
}
The lambda will always run in the UI thread, because the Progress object was created in that thread.
The advantage of this technique is that it allows decoupling the UI-related stuff from the background-related work. It was invented primarily for facilitating reporting progress from asynchronous operations, but it can be useful in multithreading too.
The following is my background worker thread
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread t1 = new Thread(Thread1);
t1.Start();
Thread t2 = new Thread(Thread2);
t2.Start();
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
}
}
Thread1 code is as follows
static void Thread1()
{
int nofiles=0;
int returned = checkforfolderthread(1);
int startvalue = 0;
int stopvalue = 5000;
if (returned == 1)
{
nofiles = countfiles();
startvalue = startvalue + (nofiles - 1) * 1000;
stopvalue = stopvalue - startvalue;
}
repeat(startvalue, stopvalue,1,nofiles-1);
}
Function called from a thread is as follows
static void repeat(int ini, int fin, int threadno, int startadd)
{
int i, j;
for (j = ini; j < ini + fin; j += 1000)
{
StringBuilder sb = new StringBuilder();
for (i = j; i < j + 1000; i += 100)
{
WebClient wc = new WebClient();
string add = System.String.Format("http://www.colourlovers.com/api/colors/new?numResults=100&resultOffset={0}", i);
try
{
string tobeadded = wc.DownloadString(add);
sb.AppendLine();
sb.Append(tobeadded);
}
catch (Exception)
{
break;
}
}
string folderpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string filename = System.String.Format("DownloadPalette\\Thread{0}\\color{1}.xml",threadno,startadd);
string location = Path.Combine(folderpath, filename);
File.WriteAllText(location, sb.ToString());
startadd = startadd + 1;
}
}
What I would want to do is continuously update a progressbar after each for i loop is completed.
But I cannot access the progressbar from this function running in the background thread.
Please Help me
You miss this method..
// Back on the 'UI' thread so we can update the progress bar
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
According on this reference : BackgroundWorker and ProgressBar demo
You should use invoke, as described here: http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
First create a method to update the progressbar(create it on the main thread GUI):
private void UpdateBar()
{
//your code to update
}
Then create a delegate and pass you method to it(this code is also for main thread GUI):
private delegate void UpdateProgressBarDelegate();
private UpdateProgressBarDelegate UpdateProgressBarDelegate_Object;
UpdateProgressBarDelegate_Object = new UpdateProgressBarDelegate(this.UpdateBar);
Now update it from another thread like this:
progressbar.Invoke(UpdateProgressBarDelegate_Object);
Here we are calling the delegate object which will call UpdateBar method on GUI thread with a safe thread call.
If you need to update more than just the progressbar value you could call a method and checking if an invoke is required. An invoke is required if you want to access a UI object from a separate thread.
private void updateProgress(object sender, int count, int total)
{
if (base.InvokeRequired)
{
base.Invoke(new ProcessCountHandler(this.updateProgress), new object[] { sender, count, total });
}
else if (count <= this.progressBar1.Maximum)
{
this.progressBar1.Value = count;
this.CompletedCount.Text = count.ToString("N0") + " of " + total.ToString("N0");
}
}
I am trying to move as much processing out of the UI thread on my Windows Phone app. I have some code that is being executed when I click on a button. The code is conceptually similar to the code below.
private int Processing(int a, int b, int c) {
this.A = this.moreProcessing(a);
this.B = this.moreProcessing(b);
this.C = this.moreProcessing(c);
int newInt = /* ... */
return newInt;
}
public void Button_Click(object sender, EventArgs args) {
var result = Processing(1, 2, 3);
this.MyTextBox.Content = result;
}
That would be very easy to move the execution on that code on a thread if the Processing method wasn't setting/getting global state variables.
How do I make sure that only one thread at a time is running in the right sequence? Right now it is easy since the processing code runs on the UI thread. The nice thing about the UI thread is that it guarantee me that everything runs in the right order and one at a time. How do I replicate that with threads?
I could refactor the entire code to have almost no global state, but cannot necessarily do that right now. I could also use lock, but I am just wondering if there's a better way. The processing I am doing isn't super heavy. However, I sometime see some lag in the UI and I want to keep the UI thread as free as possible.
Thanks!
There are a few approaches.
If you intend to fire up a new thread for every Button_Click event, then indeed you could have multiple threads that wish to write to the same variables. You can solve that by wrapping the access to those variables in a lock statement.
Alternatively, you could have one thread always running dedicated to the Processing thread. Use a BlockingCollection to communicate between the UI thread and the Processing thread. Whenever a Button_Click happens, place the relevant info on the BlockingCollection, and have the Processing thread pull work items off of that BlockingCollection.
Untested code that should be close to OK:
class ProcessingParams // Or use a Tuple<int, int, int>
{
public int A { get; set; }
public int B { get; set; }
public int C { get; set; }
}
BlockingCollection<int> bc = new BlockingCollection<int>();
private int Processing() {
try
{
while (true)
{
ProcesingParams params = bc.Take();
this.A = this.moreProcessing(params.A);
this.B = this.moreProcessing(params.B);
this.C = this.moreProcessing(params.C);
int newInt = /* ... */
return newInt; // Rather than 'return' the int, place it in this.MyTextBox.Content using thread marshalling
}
}
catch (InvalidOperationException)
{
// IOE means that Take() was called on a completed collection
}
}
public void Button_Click(object sender, EventArgs args) {
//var result = Processing(1, 2, 3);
bc.Add (new ProcessingParams() { A = 1, B = 2, C = 3 };
//this.MyTextBox.Content = result;
}
When your application closes down, remember to call
bc.CompleteAdding(); // Causes the processing thread to end
A very simple solution is to use a BackgroundWorker. It allows you to offload your work to a background thread and notify you when it is complete. (see below for another option)
void Button_Click(object sender, EventArgs args)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
e.Result = Processing(1, 2, 3);
};
worker.RunWorkerCompleted += (s1, e1) =>
{
MyTextBox.Content = e1.Result;
MyButton.IsEnabled = true;
};
// Disable the button to stop multiple clicks
MyButton.IsEnabled = false;
worker.RunWorkerAsync();
}
Another option is to get your code ready for the next version of Windows Phone and start using the Task Parallel Library. TPL is available with .Net4, but is not available with Windows Phone. There are some NuGet packages that do support Silverlight and Windows Phone. Add one of these packages to your project and you can change your code to (syntax may not be 100% correct):
private Task<int> ProcessAsync(int a, int b, int c)
{
var taskCompletionSource = new TaskCompletionSource<int>();
var task = Task.Factory.StartNew<int>(() =>
{
// Do your work
return newInt;
}
task.ContinueWith(t => taskCompletionSource.SetResult(t.Result));
return taskCompletionSource.Task;
}
void Button_Click(object sender, EventArgs args)
{
// Disable the button to prevent more clicks
MyButton.IsEnabled = false;
var task = ProcessAsync(1,2,3);
task.ContinueWith(t =>
{
MyTextBox.Content = t.Result;
MyButton.IsEnabled = true;
});
}
Try this:
public void Button_Click(object sender, EventArgs args)
{
Button.Enabled = false;
ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundProcessing));
}
private void BackgroundProcessing(object state)
{
var result = Processing(1, 2, 3);
// Call back to UI thread with results
Invoke(new Action(() => {
this.MyTextBox.Content = result;
Button.Enabled = true;
}));
}
private int Processing(int a, int b, int c)
{
this.A = this.moreProcessing(a);
this.B = this.moreProcessing(b);
this.C = this.moreProcessing(c);
int newInt = /* ... */
return newInt;
}
I'm having an application with a progress bar and a buttom.
When the button clicked the progress bar value will get increased, here is the source code,
private void Run()
{
progressBar1.Maximum = 1000;
progressBar1.Minimum = 0;
progressBar1.Step = 1;
for (int l_nIndex = 0; l_nIndex < 1000; l_nIndex++)
{
progressBar1.Value++;
Thread.Sleep(10);
}
}
private void button1_Click(object sender, EventArgs e)
{
Run();
}
so when i run the application, the progress bar value is getting increased, but when i try to move the window its not responding.
I can not run it in a normay thread way - it will throw Cross-Thread error.
so i changed the code like,
private void Run()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(this.Run));
}
else
{
progressBar1.Maximum = 1000;
progressBar1.Minimum = 0;
progressBar1.Step = 1;
for (int l_nIndex = 0; l_nIndex < 1000; l_nIndex++)
{
progressBar1.Value++;
Thread.Sleep(10);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Thread myThread = new Thread(new ThreadStart( Run));
myThread.Start();
}
Now i can able to move the winodow, but when i move the progress bar is stopped, and when i release the mouse button its resuming. So still the execution is in UI Thread.
How to handle it in a better way.Please help me to do this .
Invoke() works by running the given delegate from the UI thread. So if you use Invoke() to run your entire method, then your entire method runs from the UI thread.
Instead, you should be doing your actual work in the other thread, and just performing UI updates in the UI thread, by just Invoke()ing the little bits of code that perform the updates.
One easy way to do this is to use the BackgroundWorker class built into the standard library.
This has been answered here - in your case the code should look something like:
this.BeginInvoke((Action)(() => progressBar1.Value++));