|
Thanks PeterTheSwede!
Great reply! I am interested to discuss further with you.
I am very interested in two statements you mentioned before,
---------------------
1.
The reason behind all this is that it is usually expensive (in terms of performance or memory) for an enumerator (see below) to handle additions/deletions/reordering during its lifetime.
2.
As the enumerator has to know where it is and where it is going in the collection, handling inserts, deletes and reordering would force it to - for example - copy a reference for each element into a temporary array when it is created (when the loop starts), and then step through that array.
---------------------
Especially for item 2, I think it is the reason why add/remove/reorder during foreach is expensive, but my question is I can not figure out why from the implmentation level it is expensive, for example, why do we need to "copy a reference for each element into a temporary array when it is created"? Could you clarify or give more description please?
The reason why I am confused is, for example, delete during foreach is not hard to implement and should not be expensive,
(suppose we are using linked list to store a list of key collections of a Dictinoary)
1. if delete an element which is already iterated, just remove and step next in next iteration;
2. if delete an element which is not iterated yet, just remove and step next in next iteration;
3. if delete an element which is the element which is currently iterated now, just remove the current and step to next iteration.
regards,
George
|
|
|
|
|
George_George wrote: ...delete during foreach is not hard to implement and should not be expensive
You are perfectly right - there are scenarios where this isn't expensive - a linked list is a perfect example. A lazy-loading collection class that behaves like an ADO recordset would be another.
The point, however, is that this isn't always the case - it depends on how the collection class is implemented internally.
Look at this example (not tested, may have typos):
private List<SomeType> myList = new List<SomeType>
public System.Collections.Generic.IEnumerator<SomeType> GetEnumerator()
{
for (int i = 0; i < myList.Length; i++)
{
yield return myList[i];
}
}
public System.Collections.Generic.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
This is probably what most custom collection classes look like (at least mine do). Since I know that people don't mess with my collection from within a foreach loop, I don't even have to think about what happens if elements get deleted. As you can see, I will miss the next element in that case - if you delete element 2 in {1, 2, 3, 4}, the next element yielded will be 4, not 3. Also, if someone tried to optimize it by storing myList.Length in a variable (which would be quite possible), the for loop would even throw an exception (index out of range) before finishing the loop.
If you look at it for a while, you see that there is no really simple way to fix this (allow deletes) without using something that isn't a List. And let's say - for the sake of argument - that I have to use a list (perhaps because I get the list from some internal class that only returns a list, and a conversion would be costly).
So... although you could see the "rules" as more of recommendations, they are generally accepted, so you can never assume that they don't apply. Also, you will cause confusion among your peers if you violate them, even if the collection class you're using explicitly permits it (unless you make that very clear in a comment).
Peter the small turnip
(1) It Has To Work. --RFC 1925[^]
|
|
|
|
|
Thanks Peter,
Your reply is so great! One question, you mentioned -- "custom collection classes", do you mean the class like List/Dictionary or the class like SomeType in your sample?
regards,
George
|
|
|
|
|
Thanks. And sorry - the word "custom" usually means "not standard", so yes, I meant the class SomeType in my example. Or any other collection class not in the .NET framework.
Then again, what I say is probably true even for the "standard" classes (the ones in the framework), unless the documentation explicitly tells you otherwise.
Peter the small turnip
(1) It Has To Work. --RFC 1925[^]
|
|
|
|
|
Great Peter!
Question answered.
regards,
George
|
|
|
|
|
If you look at it for a while, you see that there is no really simple way to fix this (allow deletes) without using something that isn't a List.
Actually, there would be. Provide a means of indicating an object is no longer meaningful (e.g. for an array-based dictionary, set the key to nothing), and make the 'add' routine (which may not be run during an enumeration) perform the actual deletions. Note that this would not only allow for convenient deletion of objects from within an enumerator--it would also allow the consolidation of multiple deletions that are performed without an intervening addition.
|
|
|
|
|
George_George wrote: for example, why do we need to "copy a reference for each element into a temporary array when it is created"? Could you clarify or give more description please?
Expanding on my previous example:
private List<SomeType> myList = new List<SomeType>
public System.Collections.Generic.IEnumerator<SomeType> GetEnumerator()
{
SomeType[] elems = myList.ToArray();
for (int i = 0; i < elems.Length; i++)
{
yield return elems[i];
}
}
public System.Collections.Generic.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
With the ToArray() call (which is a memory allocation and a shallow copy of the list - an expensive operation if the list is large), I am now totally immune to any changes of the list during the foreach.
And... without spending more time on it than I would like when building my own collection class, I can't think of a cheaper way to solve this (again assuming that I must use a list internally, and cannot use a linked list, for example). And again... the purpose of the rule (to not mess with the contents of a collection from within a foreach loop) is that I shouldn't have to spend time thinking about this when building an enumerator...
Any clearer?
Peter the small turnip
(1) It Has To Work. --RFC 1925[^]
|
|
|
|
|
Hello everyone,
I have not found any support document about whether the operation of reference variable assignment is atomic or not.
For example, foo1 and foo2 are both reference variable of type Foo. Is foo1 = foo2 atomic? Any support documents?
thanks in advance,
George
|
|
|
|
|
Yes, assigning a reference is an atomic operation.
ECMA-334 C# Language Specification:
"12.5 Atomicity of variable references
Reads and writes of the following data types shall be atomic: bool , char , byte , sbyte , short , ushort , uint , int , float , and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including long , ulong , double , and decimal , as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement."
Despite everything, the person most likely to be fooling you next is yourself.
|
|
|
|
|
Great Guffa!
Question answered.
regards,
George
|
|
|
|
|
Guffa wrote: Yes, assigning a reference is an atomic operation.
DANGER WILL ROBINSON! DANGER WILL ROBINSON!
An assignment of a reference type consists of an atomic read followed by an atomic write. The assignment as a whole is not atomic.
Assume var2 starts == 1.
Thread 1:
var1 = var2;
Thread2:
var2 = 2;
var1 = 3; If assignments were atomic, var1 might end up equal to 2 or 3, but it could not possibly end up equal to anything else (such as 1). Since they are not atomic, however, thead 1 decomposes as "temp = var2; var1 = temp;". The operations could thus be sequenced as "temp = var2 [1]; var2 = 2; var1 = 3; var1 = temp [1];"
It would be possible to write a generic class to support true atomic assignments. Given the existence of Interlocked.CompareExchange it could even be done without locks or spin-waits(*) in such a way that the total time required for any number of operations would be bounded (the time required for any particular operation would only be bounded if the number of other operations was limited).
(*) A thread may have to retry an operation up to 'n' times if other threads perform 'n' operations during its execution; it is never necessary, however, for a thread to wait for another thread to do something.
I don't know if there are any nice algorithms to synthesize atomic assignment given atomic compare-and-exchange. The best lock-free approach I can come up with is pretty nasty, but it would work.
|
|
|
|
|
supercat9 wrote: An assignment of a reference type consists of an atomic read followed by an atomic write.
No, it doesn't. An assignment is only a write operation.
What you are talking about is a complete statement, which in this case is the evaluation of an expression and the assignment of the result. The complete statement is of course not an atomic operation.
Despite everything, the person most likely to be fooling you next is yourself.
|
|
|
|
|
Guffa wrote: No, it doesn't. An assignment is only a write operation.
The original question was Is foo1 = foo2 atomic?. You answered in the affirmative, without giving any indication that "foo1 = foo2" represents two operations.
Guffa wrote: The complete statement is of course not an atomic operation.
Whether the complete statement is atomic or not depends on the implementation. Some microprocessors and microcontrollers do allow atomic memory-to-memory moves, at least in non-paged implementations (the 68020-68040 even allow it in paged implementations, though I doubt any compiler would use the CAS2 instruction for a normal assignment). Your statement could reasonably have been taken to suggest that the processors that run Windows will perform a statement like "foo1 = foo2" atomically. I think it's very important to point out clearly and unambiguously that they will not.
|
|
|
|
|
As an addition to what Guffa said if you take for example:
Foo foo = new Foo();
you should know that first the right side of the assignment is evaluated. So that first the Foo object is constructed entirely and then assigned to foo. So this is also an atomic operation.
|
|
|
|
|
Great Zoltan!
Question answered.
regards,
George
|
|
|
|
|
I have two threads, one UI thread and one Background thread..
I want to keep a track of the background thread so that I may kill my UI thread (a tray icon) when the background thread has finished...
Please keep in mind mine is a console application.
Please review my code for more details..
class Program
{
static void Main(string[] args)
{
Thread[] threads = new Thread[2];
threads[0] = new Thread(new ThreadStart(UIMethod));
threads[1] = new Thread(new ThreadStart(BackGroundMethod));
threads[0].SetApartmentState(ApartmentState.STA);
threads[0].Start();
threads[1].Start();
}
public static void UIMethod()
{
Tray obj = new Tray();
obj.GenerateTrayIcon();
Application.Run();
}
public static void BackGroundMethod()
{
}
}
Any suggestions with some code would be highly appreciated..
Thanks.
|
|
|
|
|
Either call Join() on the threads, or use signalling with WaitHandles.
|
|
|
|
|
I dont know about them..
Could you post some sample code with respect to the code in my original code?
Thanks.
|
|
|
|
|
Ok
Thread[] threads = new Thread[2];
threads[0] = new Thread(new ThreadStart(UIMethod));
threads[1] = new Thread(new ThreadStart(BackGroundMethod));
threads[0].SetApartmentState(ApartmentState.STA);
threads[0].Start();
threads[1].Start();
threads[1].Join();
|
|
|
|
|
But this dosent server the purpose..
The UI thread still is waiting for user response although the background thread has finished.
I want to kill the UI thread when the background thread has finished executing..
|
|
|
|
|
When you call Join() on the thread which executes background method, the current thread will be blocked until background method finishes executing. Just down to the Join() call, you can write code to kill the other one or kills the icon.
|
|
|
|
|
|
If your goal is to have the user-interface thread run normally until the background thread dies, I would suggest having a 'quitNow' variable which is set by the background thread and polled in the main thread. The simplest approach to accomplishing that would be to have a timer that checks the 'quitNow' variable every 100ms or so. If you want to avoid the CPU overhead of doing that, you might be able to have the the timer disabled until the background thread enables it to signal its completion. I'm not sure if timer.enabled can be accessed between threads, though. If it can't, you may have to do something like:
Sub EnableKillMeTimer
If InvokeRequired then
BeginInvoke(New MethodInvoker(AddressOf EnableKillMeTimer))
Else
tmrKillMe.Enabled = True
Endif
End Sub While an approach like the above could probably be used to have the main form execute me.close, using a timer could make things a little nicer in case the main form doesn't want to close quite instantly.
|
|
|
|
|
Hey,
I have a Word 2003 Plug-in written in VS 2008.
In the plug-in startup method I create a button (or find it if it's already made) and assign an eventhandler to it:
m_SaveButton.Click += new Microsoft.Office.Core._CommandBarButtonEvents_ClickEventHandler(SaveDoc_Click);
m_SaveButton is a global variable (Office.CommandBarButton m_SaveButton; )
Everything works fine until I have opened a document, then the eventhandlers for my button is not called anymore.
Any idea what's going on?
- Anders
|
|
|
|
|
Suppose we got the following code snippet:
var a = new {i=1, j=2};
How to assign a value for the specific field of that variable? I mean, something like this:
a.i = 5;
thanks for help
|
|
|
|
|