[RESOLVED] SynchronizationContext Trouble
Hey all,
I'm trying to change/execute the example http://www.codeproject.com/KB/thread...onContext.aspx
I think the problem may be in my translation to Vb....
I've posted my project below. I'm getting a cross-threading error and I thought the point of all of this was to avoid that. (yes I realize I could just delegate... I'm trying to do something more complicated but am using this as a test bed)
Thanks in advance...
The following is just a form with a button and list box.
Code:
Imports System.Threading.Thread
Imports System.Threading
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' let's see the thread id
Dim id As Integer
Dim uiContext As SynchronizationContext
Dim thread As Thread
id = thread.CurrentThread.ManagedThreadId
Trace.WriteLine("Button1_Click thread: " + id.ToString)
' grab the sync context associated to this
' thread (the UI thread), and save it in uiContext
' note that this context is set by the UI thread
' during Form creation (outside of your control)
' also note, that not every thread has a sync context attached to it.
uiContext = SynchronizationContext.Current
' create a thread and associate it to the run method
thread = New Thread(AddressOf Run)
' start the thread, and pass it the UI context,
' so this thread will be able to update the UI
' from within the thread
thread.Start(uiContext)
End Sub
Private Sub Run(ByVal state As Object)
Dim id As Integer
Dim uiContext As SynchronizationContext
id = thread.CurrentThread.ManagedThreadId
Trace.WriteLine("Run thread: " + id.ToString)
' grab the context from the state
uiContext = state
For i = 0 To 1000
Thread.Sleep(10)
' use the ui context to execute the UpdateUI method,
' this insure that the UpdateUI method will run on the UI thread.
uiContext.Post(UpdateUI(state), "line " + i.ToString())
Next
End Sub
Private Function UpdateUI(ByVal state As Object)
Dim id As Integer
Dim text As String
id = Thread.CurrentThread.ManagedThreadId
Trace.WriteLine("UpdateUI thread:" + id.ToString)
text = state.ToString
mListBox.Items.Add(text)
Return ""
End Function
End Class
Re: SynchronizationContext Trouble
You don't need to pass the uiContext to the thread. Variables declared outside of methods are not tied to any particular thread, and are therefore accessible from any of them. Therefore, since uiContext was declared at class scope, the thread will see it just as well as any other code.
EDIT: Oops, you declared the uiContext as a local variable. I assumed you had it at class scope because that is what I always do. In your case, passing it along seems like the reasonable thing to do, as I assume that you class scope wouldn't suffice.
What are your traces showing you? Are they what you expect?
Also, where are you getting the exception? I would guess that it is when you add to the listbox, as that is the only control you appear to be manipulating, but might as well be certain.
Re: SynchronizationContext Trouble
The first thing you should do is turn Option Strict On and leave it On for this and every other project. I just copied your code and fixed the errors that Option Strict On flagged and it worked fine.
Code:
Imports System.Threading.Thread
Imports System.Threading
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' let's see the thread id
Dim id As Integer
Dim uiContext As SynchronizationContext
Dim thread As Thread
id = thread.CurrentThread.ManagedThreadId
Trace.WriteLine("Button1_Click thread: " + id.ToString)
' grab the sync context associated to this
' thread (the UI thread), and save it in uiContext
' note that this context is set by the UI thread
' during Form creation (outside of your control)
' also note, that not every thread has a sync context attached to it.
uiContext = SynchronizationContext.Current
' create a thread and associate it to the run method
thread = New Thread(AddressOf Run)
' start the thread, and pass it the UI context,
' so this thread will be able to update the UI
' from within the thread
thread.Start(uiContext)
End Sub
Private Sub Run(ByVal state As Object)
Dim id As Integer
Dim uiContext As SynchronizationContext
id = thread.CurrentThread.ManagedThreadId
Trace.WriteLine("Run thread: " + id.ToString)
' grab the context from the state
uiContext = DirectCast(state, SynchronizationContext)
For i = 0 To 1000
Thread.Sleep(10)
' use the ui context to execute the UpdateUI method,
' this insure that the UpdateUI method will run on the UI thread.
uiContext.Post(AddressOf UpdateUI, "line " + i.ToString())
Next
End Sub
Private Function UpdateUI(ByVal state As Object) As String
Dim id As Integer
Dim text As String
id = Thread.CurrentThread.ManagedThreadId
Trace.WriteLine("UpdateUI thread:" + id.ToString)
text = state.ToString
mListBox.Items.Add(text)
Return ""
End Function
End Class
Re: SynchronizationContext Trouble
Turning it on from options didn't do anything. But when I added option strict on at the top it did an awesome job of showing me all that was wrong.
Thanks so much!
I'm adding it to all of my projects and am finding lots of casting errors.
Re: SynchronizationContext Trouble
Last Question:
If I had started another thread in this example and instead of jumping into the UI thread I wanted to jump into a previously created thread...
i.e. in this call
Code:
uiContext = SynchronizationContext.Current
What would I do if I wanted the SC of thread A, instead of the current UI thread?
(here is the fictitious A code)
Code:
Dim A As Thread
A = New System.Threading.Thread(AddressOf doThreadA)
A.Start()
Re: SynchronizationContext Trouble
Whichever thread you get SynchronizationContext.Current on, that's the thread that that SynchronizationContext object will post to. If you create a new thread you can access SynchronizationContext.Current and assign the value to a variable, then other threads can use that variable to post method calls to that thread.
Re: SynchronizationContext Trouble
Ok,
Using your fixed example. (thank you again btw)
I placed the call
Code:
testContext = SynchronizationContext.Current
in the Run subroutine. This should be on the thread Thread. (wow that was a bad name...)
But when I check it in runtime it gives a value of Nothing. (full code below)
Code:
Public Class Form1
Private testContext As SynchronizationContext
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
' let's see the thread id
Dim id As Integer
Dim uiContext As SynchronizationContext
Dim thread As Thread
id = thread.CurrentThread.ManagedThreadId
Trace.WriteLine("Button1_Click thread: " + id.ToString)
' grab the sync context associated to this
' thread (the UI thread), and save it in uiContext
' note that this context is set by the UI thread
' during Form creation (outside of your control)
' also note, that not every thread has a sync context attached to it.
uiContext = SynchronizationContext.Current
' create a thread and associate it to the run method
thread = New Thread(AddressOf Run)
thread.IsBackground = True
' start the thread, and pass it the UI context,
' so this thread will be able to update the UI
' from within the thread
thread.Start(uiContext)
End Sub
Private Sub Run(ByVal state As Object)
Dim id As Integer
Dim uiContext As SynchronizationContext
testContext = SynchronizationContext.Current
id = thread.CurrentThread.ManagedThreadId
Trace.WriteLine("Run thread: " + id.ToString)
' grab the context from the state
uiContext = DirectCast(state, SynchronizationContext)
For i = 0 To 1000
Thread.Sleep(10)
' use the ui context to execute the UpdateUI method,
' this insure that the UpdateUI method will run on the UI thread.
uiContext.Post(AddressOf UpdateUI, "line " + i.ToString())
Next
End Sub
Private Sub UpdateUI(ByVal state As Object)
Dim id As Integer
Dim text As String
id = Thread.CurrentThread.ManagedThreadId
Trace.WriteLine("UpdateUI thread:" + id.ToString)
text = state.ToString
mListBox.Items.Add(text)
End Sub
End Class
Re: SynchronizationContext Trouble
Interesting. I just did a bit of testing and it does indeed appear that accessing SynchronizationContext.Current on other than the UI thread retrieves a null reference. That's not what I expected but, I must admit, there were some assumptions at play. I'm not an expert on the SynchronizationContext class and it seems that noone else is either because information is scarce. That said, I can't think of a reason that you'd need to use it for other than the UI thread anyway.
Re: SynchronizationContext Trouble
Your're right on the not really needing it. It was more of a desire to have a more logical flow of data. (kind of data collection in tree form rather than everything reporting to one huge collection class and wasting UI processing time.)
Well, if anyone out there figures anything out I would appreciate it.
This article references the situation but I'm not experienced enough to understand if he is just using it to sort out if the thread is a UI or not thread or if he has a solution that would work for my wants.
Thanks again for taking the time to look at it all.
http://daveonsoftware.blogspot.com/2...ioncontex.html
Re: SynchronizationContext Trouble
I'm not really sure what problem it is that you want a solution to.
Re: SynchronizationContext Trouble
Basically I would really like to pass information from one thread to another with out any weird data synch problems.
- Nick
Re: SynchronizationContext Trouble
Quote:
Originally Posted by
nickwrs
Basically I would really like to pass information from one thread to another with out any weird data synch problems.
- Nick
But you can already do that. Passing information between threads is really no issue at all because, when we're talking about standard variables, all threads have access to all data all the time. The only real issue when it comes to threading is that you cannot access the handle of a control on any thread except the one on which it was created. That's really the only reason that you have to ensure that certain things are done on a specific thread. You've already got three ways to do that: the ISynchronizeInvoke interface in WinForms, the Dispatcher class in WPF and the SynchronizationContext class in either. Why do you need anything else?
Re: SynchronizationContext Trouble
I guess I was under the impression that if a class had more than one thread trying to access a class level variable at the same time a problem would occur.
If that isn't true, then I have no problems at all....
if it is true then I don't think I understood your last post. Sorry.
Thanks again for bearing with me.
- Nick
Re: SynchronizationContext Trouble
Quote:
Originally Posted by
nickwrs
I guess I was under the impression that if a class had more than one thread trying to access a class level variable at the same time a problem would occur.
If that isn't true, then I have no problems at all....
if it is true then I don't think I understood your last post. Sorry.
Thanks again for bearing with me.
- Nick
It is and it isn't. There's no issue having multiple threads accessing the same data. It happens all the time. What is a problem is if you have multiple threads reading and writing exactly the same data at exactly the same time, which can have serious consequences.
Consider a situation where you have two threads that both get the value of a variable, add 1 to that value, then assign the result back to the variable. Because the order in which multiple threads execute is indeterminate, that situation might yield the correct result most of the time, but sometimes it will do the wrong thing. What if one thread reads the value and increments it but gets switched out before writing the result back to the variable? The second thread will come along and read the old value, increment it and write the result back, then the first thread will pick up again and write its result back. The variable's value will only increase by 1 instead of by 2, and that could be disastrous, depending on what your app does with the value.
The solution to such issues has nothing to do with passing data between threads and everything to do with synchronising execution of threads. What you need to do is wrap the code that accesses the common data in a lock, such that a second thread can't enter the critical section before the first thread exits. This ensures that, in the case of the previous example, once a thread starts reading the variable, no other thread can read it until the first thread has written to it.
Re: SynchronizationContext Trouble
Could a lock be as simple as a class level flag? don't change this variable unless this flag is true etc.
Re: SynchronizationContext Trouble
Quote:
Originally Posted by
nickwrs
Could a lock be as simple as a class level flag? don't change this variable unless this flag is true etc.
No, because the flag itself has the same problem. One thread comes along and reads the flag and sees it can continue and then gets preempted by a second thread that also reads the flag and continues. Both threads then toggle the flag and continue to access the common data. The flag itself is part of the common data so you've simply moved the probelm.
Thread synchronisation functionality is built into the Framework and the language. The simplest form of synchronisation is the SyncLock statement.
Re: SynchronizationContext Trouble
Got it. I'll look that up. Thanks again.
Re: [RESOLVED] SynchronizationContext Trouble
By the way, it is precisely the problem with that flag idea that lead to the Mutex, which is something like a kernel-level flag. You can find Mutexes in the framework, but Synclock and Monitor are much faster in practice. As for those two, Synclock is a Monitor wrapped in some error handling to ensure that the Monitor is released. So use a Synclock where you can, use a Monitor only where a Synclock won't work (which is quite rare), and use a Mutex only where neither of the other two will work. However, since a Mutex is a kernel level object, the places that it would be superior can be relatively easily enumerated: Those places where you need to synchronize data access between different running programs. Not a common scenario.