 |
|
 |
I have loaded many images in a listview using ImageList in c#. When many many images are loaded then it takes a long time. So I call the method in backgroundworker. In the backgroundworker I had to add images to ImageList and add ImageList to ListView. So I have used safeinvoke() method listView1.SafeInvoke(d=>d.Items.Add(item)).
. Everything works fine. Images are displayed one by one in the listview. But the problem is the listview is continuously refreshing when a new item is added. This refreshing seems disturbing to the user. When I have added the item without using background worker (listView1.Items.Add(item)) then it worked fine. It was added one after another smoothly without refreshing. I want to add item without refreshing the listview.
Where is the problem? Is the problem of backgroundworker or the SafeInvoke () method? How to solve it?
sample code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string[] fileNames = Directory.GetFiles(@"E:\Image");
foreach (string flName in fileNames)
{
Bitmap btmap = (Bitmap)Image.FromFile(flName);
scanNo++;
//imageList.Images.Add(newBtmap); when backgroundworker was not used
this.SafeInvoke(d=>d.imageList.Images.Add(newBtmap));
ListViewItem item;
item = new ListViewItem();
item.ImageIndex = scanNo - 1;
item.Text = scanNo.ToString();
// listView1.Items.Add(item); when backgroundworker was not used
listView1.SafeInvoke(d=>d.Items.Add(item));
}
}
|
|
|
|
 |
|
 |
before starting the backgroundworker you need to use
imageList.BeginUpdate();
... backgroundworker work...
imageList.EndUpdate();
|
|
|
|
 |
|
 |
Thinking about threads running in the context of a form or control doing work possibly using UI properties then setting UI properties or calling Functions that set UI properties this is a very elegant solution.
I have modified it a little based on my observations of thread usage within such context. I.E. I have deliberately confined to usage on Control derived classes to clarify usage.
public static class ControlExtensions
{
public static TResult SafeInvoke<TResult>(this Control isi, Func<TResult> call)
{
return isi.InvokeRequired ? (TResult)isi.Invoke(call, null) : call();
}
public static void SafeInvoke(this Control isi, Action action)
{
if (isi.InvokeRequired)
isi.Invoke(action, null);
else
action();
}
}
In UI Form
private void button1_Click(object sender, EventArgs e)
{
Thread thrd = new Thread(Start);
thrd.Start();
}
private void Start()
{
string t = this.SafeInvoke(() => button1.Text);
this.SafeInvoke(() => button1.Text = "OMG");
this.SafeInvoke(() => button2.Text = button1.Text);
this.SafeInvoke(() => MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString())); MessageBox.Show(Thread.CurrentThread.ManagedThreadId.ToString()); }
|
|
|
|
 |
|
 |
simple and to the point
Thank you
|
|
|
|
 |
|
 |
I've written several programs that had to deal with updating from a back-ground task. I always dread making all the special routines, this solution worked beautifully! It's wonderful! I don't understand how it works , but I don't care!
-Matt
|
|
|
|
 |
|
 |
I am a beginner at threading. the demo form does not close nicely. it gives an error at the line:
object endResult = isi.EndInvoke(result); return (TResult)endResult;
if the calc is running.
What is the way to handle this?
I added a done button to the form with form.close() as its click code, and the error did not happen, but the calc must finish before the form will close.
How do I stop a thread if needed, maybe in the form_closing event.
thanks
modified on Thursday, December 16, 2010 2:17 PM
|
|
|
|
 |
|
 |
you should send a message to the running code in the thread that the program is closing(by setting some bool). then you wait till the thread exits before letting the form close.
It's also wise to set the IsBackground property of the thread object to true. That way you prevent that the thread still runs even when the form has closed. All background threads are automatically closed when no non-background thread are active anymore.
|
|
|
|
 |
|
 |
I think if you could make the get version even simpler by using Invoke instead of BeginInvoke/EndInvoke:
public static TResult SafeInvoke<T, TResult>(this T isi, Func call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) {
return (TResult)isi.Invoke(call, new object[] { isi });
} else {
return call(isi);
}
}
|
|
|
|
 |
|
 |
Your simplification works for me, does anyone see any drawbacks?
Regards,
John.
PS there was a typo in you post "Func call", here is a corrected version
public static TResult SafeInvoke<T, TResult>(this T isi, Func<T, TResult> call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired)
{
return (TResult)isi.Invoke(call, new object[] { isi });
}
else
return call(isi);
}
|
|
|
|
 |
|
 |
Further to my previous reply, this may need to be considered, it comes from: http://kristofverbiest.blogspot.com/2007/02/avoid-invoke-prefer-begininvoke.html
Avoid Invoke(), prefer BeginInvoke()
This is another post in a series that deals with asynchronous behaviour (see also here and here).
The difference between Control.Invoke() and Control.BeginInvoke() is:
•BeginInvoke() will schedule the asynchronous action on the GUI thread. When the asynchronous action is scheduled, your code continues. Some time later (you don't know exactly when) your asynchronous action will be executed.
•Invoke() will execute your asynchronous action (on the GUI thread) and wait until your action has completed.
A logical conclusion is that a delegate you pass to Invoke() can have out-parameters or a return-value, while a delegate you pass to BeginInvoke cannot (you have to use EndInvoke to retrieve the results).
My advice is: always use BeginInvoke() if you can. Reasons:
•Because Invoke() causes one thread to wait for another thread, it is likely to cause deadlocks. Imagine that your application has a dedicated worker thread that uses Invoke() to execute some work on the GUI thread. Now image that during this work, your GUI code needs to execute something on the worker-thread. It will be blocked forever because your worker-thread is already busy waiting for your GUI-thread.
In this simple example it might be easy to spot the problem. In a big application developed by many people it will not always be this easy. Therefore it is better to find a pattern that avoids blocking altogether.
Note that in this example you might think that a threadpool solves your problem, because then you have multiple workerthreads. However the problem will still occur under high load, this is called threadpool starvation and it is a very complex problem. Because it only occurs under high load, it is a very dangerous thing that is very difficult to debug.
•Invoke() will cause forced threadswitches that will decrease the scalability of your application.
First consider BeginInvoke: when it is called, a delegate is scheduled to be executed on the other thread. However your current thread will continue until the operating system decides that it's done enough work. Only then will the other thread (GUI thread) be executed, and your delegate will be run. If other delegates were also scheduled using BeginInvoke(), they will be executed as well.
Now consider Invoke: when you call it, the OS immedeately has to stop your current thread and schedule the other thread.
When more than one Invoke()-statement is done, a threadswitch will be needed every single time (whereas multiple BeginInvoke-requests can be bundled in one threadswitch).
Excessive threadswitches will seriously hurt performance when your application is under a high load.
Conclusion: avoid using Invoke().
Regards,
John.
|
|
|
|
 |
|
 |
In general your right, but in the example the calling thread is immediately waiting in EndInvoke for the end of the operation. So the thread is waiting anyway and it makes no difference. No thread dead locks will be avoided.
I'm not sure about the second point (forced thread switches) maybe BeginInvoke/EndInvoke performs better.
Regards,
Jürgen
|
|
|
|
 |
|
 |
I have decided to do this with both Methods:
public static TResult SafeInvoke<T, TResult>(this T isi, Func<T, TResult> call, bool BeginInvoke = true) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired)
{
if (BeginInvoke) {
IAsyncResult result = isi.BeginInvoke(call, new object[] { isi });
object endResult = isi.EndInvoke(result);
return (TResult)endResult;
}
else
{
return (TResult)isi.Invoke(call, new object[] { isi }); }
}
else
return call(isi);
}
I used it in an application that sends very large files over the serial port. I looped the connection back so that much data was being sent and received and cpu usage was 100%. The received data was sent back to the UI thread using this code. With the BeginInvoke set to true as is default, there was much data loss. With BeginInvoke set to false there was not data loss. This at least shows there will be instances where the option is handy. Also when marshalling back to the UI thread I prefer some operations to complete before continuing. For example I frequently change the cursor and it is nice to be sure it happens at the outset rather then being scheduled for some time which can vary with heavy cpu usage as described.
Regards,
John.
|
|
|
|
 |
|
 |
Amazing
Thank you for posting!
Anthony
|
|
|
|
 |
|
 |
This will certainly come in handy. Thank you.
|
|
|
|
 |
|
 |
Remarkable piece of code! This one deserves a 6, not just a 5!
|
|
|
|
 |
|
 |
After reading this great article, here is the VB implementation I now use:
Label1.SafeInvoke(Function(x) InlineAssignHelper(x.Text, "some text"))
Dim textFromLabel As String = Label1.SafeInvoke(Function(x) x.Text)
Module ExtentionMethod
Public Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function SafeInvoke(Of T As ISynchronizeInvoke, TResult)(ByRef isi As T, ByRef [call] As Func(Of T, TResult)) As TResult
If isi.InvokeRequired Then
Dim result As IAsyncResult = isi.BeginInvoke([call], New Object() {isi})
Dim endResult As Object = isi.EndInvoke(result)
Return DirectCast(endResult, TResult)
Else
Return [call](isi)
End If
End Function
End Module
-Shaun
modified on Tuesday, November 16, 2010 3:44 PM
|
|
|
|
 |
|
 |
Have you tried to implement this using ISynchronizeInvoke instead of control in VB code?
I have conversed the latest sample code from C# to VB but it does not work.
Thanks
modified on Friday, October 29, 2010 4:45 PM
|
|
|
|
 |
|
 |
For sure I will click you Donate button twice.
Thanks again
dasdsa
|
|
|
|
 |
|
 |
Hello,
Here's a few adaptations to your code i wanted to share.
1) I now hook the extension on ISynchronizeInvoke type, instead of only Control type. This widens its use. After all, this is exactly what the ISynchronizeInvoke interface is made for.
2) The original implementation was only safe in cross-thread situations. So, I added a check on InvokeRequired to make it work in all situations, cross-thread or not...
3) There is not really a need for 2 different method names. Based on the different signatures, the compiler can distinguish which overload to use. So, you can actually cover both 'void' and 'return' cases with just one overloaded method. Which makes it more elegant i think. I.e. eliminates the need for the 'Get' specification.
Here are the adaptations:
First overload covers calls with returns. Example:
Color cl = textBox.SafeInvoke( tbx => tbx.BackColor );
int hash = textBox.SafeInvoke( tbx => tbx.GetHashCode() );
public static TResult SafeInvoke<T, TResult>( this T isi, Func<T, TResult> call )
where T : ISynchronizeInvoke
{
if ( isi.InvokeRequired )
{
IAsyncResult result = isi.BeginInvoke( call, new object[] { isi } );
object endResult = isi.EndInvoke( result );
return (TResult) endResult;
}
else
return call( isi );
}
Second overload covers void calls. Example:
textBox.SafeInvoke( tbx => tbx.BackColor = Color.White );
textBox.SafeInvoke( tbx => tbx.Show() );
public static void SafeInvoke<T>( this T isi, Action<T> call )
where T : ISynchronizeInvoke
{
if ( isi.InvokeRequired )
isi.BeginInvoke( call, new object[] { isi } );
else
call( isi );
}
regards,
Philippe
Philippe Dykmans
Software developpement
Advanced Bionics Corp.
|
|
|
|
 |
|
 |
looks even better indeed!
thx for sharing.
|
|
|
|
 |
|
 |
I've updated the article with the code. thx again for the feedback!
|
|
|
|
 |
|
 |
I just used this and it work outstanding.
It is so simple and it works!
And it is not a pain in the #$@%$.
You got a 5 from me!
Thanks
|
|
|
|
 |
|
 |
| If you change the namespace from SimpleThreadSafeCall to System.Windows.Forms, you don't have to remember the 'using SimpleThreadSafeCall' declaration. Just don't forget to reference the dll though... Philippe Dykmans
Software developpement
Advanced Bionics Corp.
|
|
|
|
 |
|
 |
This bit of Code is Excellante` !!
I have been doing things the hard way for a while and threading is not an easy thing to implement to begin with so you might just imagine my code not being the most responsive. Thank you so much for this.
I have an application where I created a Forms User Control [Basically a Form with Controls inside a Form] It requires certain functionality and I think that this will allow my application to be more responsive to the user. Thank you!!
My Humble Bow to you sir!
My Humble Bow to you sir!
My Humble Bow to you sir!
|
|
|
|
 |
|
|
 |