I couldn't really find anything on this exact topic, so please lead me toward the right direction, if a question already exists.
From what I have learned about .NET, it is not possible to access variables across different threads (please correct me if that statement is wrong, it's just what I have read somewhere).
Now in this codesample, however, it then seems that it shouldn't work:
class MyClass
{
public int variable;
internal MyClass()
{
Thread thread = new Thread(new ThreadStart(DoSomething));
thread.IsBackground = true;
thread.Start();
}
public void DoSomething()
{
variable = 0;
for (int i = 0; i < 10; i++)
variable++;
MessageBox.Show(variable.ToString());
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void SomeMethod();
{
MyClass mc = new MyClass();
}
}
When I run SomeMethod()
shouldn't .NET throw an exception, because the created object mc
is running in a different thread than the thread created within the mc
-initializer and this new thread is trying to access the local variable of mc
?
The MessageBox
shows 10
as (not) expected, but I am not sure why this should work.
Maybe I didn't know what to search for, but no threading-topic I could find, would address this issue, but maybe my idea of variables and threads is wrong.
From what I have learned about .NET, it is not possible to access variables across different threads. Please correct me if that statement is wrong, it's just what I have read somewhere.
That statement is completely false, so consider this your correction.
You probably read somewhere that local variables cannot be accessed across different threads. That statement is also false but is commonly stated. The correct statement is that local variables that are not
yield return
or yield break
)cannot be accessed by multiple threads. And even that claim is a bit dodgy; there are ways to do that with pointers and unsafe
code blocks, but it is a very bad idea to attempt to do so.
I note also that your question asks about local variables but then gives an example of a field. A field is by definition not a local variable. A local variable is by definition local to a method body. (Or constructor body, indexer body, etc.) Make sure you are clear on that. The defining characteristic of a local is not that it is "on the stack" or some such thing; the "local" part of a local is that its name is not meaningful outside of the method body.
In the general case: a variable is a storage location that refers to memory. A thread is a point of control in a process, and all threads in a process share the same memory; that's what makes them threads and not processes. So in general, all variables can be accessed by multiple threads at all times and in all orders, unless some mechanism is put in place to prevent that.
Let me say that again just to make sure it is absolutely crystal clear in your mind: the correct way to think about a single-threaded program is that all variables are stable unless something makes them change. The correct way to think about a multi-threaded program is that all variables are mutating constantly in no particular order unless something is keeping them still or well-ordered. This is the fundamental reason why the shared-memory model of multithreading is so difficult, and therefore why you should avoid it.
In your particular example, both threads have access to this
, and therefore both threads can see the variable this.variable
. You have implemented no mechanisms to prevent that, and therefore both threads can be writing and reading to that variable in any order, subject to very few constraints indeed. Some mechanisms you could have implemented to tame this behavior are:
ThreadStatic
. Doing so causes a new variable to be created on each thread.volatile
. Doing so imposes certain restrictions on how reads and writes may be observed to be ordered, and also imposes certain restrictions on optimizations made by the compiler or CPU that could cause unexpected results.lock
statement around every usage of the variable.Unless you have a deep understanding of multithreading and processor optimizations, I recommend against any option except the latter.
Now, suppose you did wish to ensure that access to the variable failed on another thread. You could have the constructor capture the thread ID of the creating thread and stash it away. Then you could access the variable via a property getter/setter, where the getter and setter check the current thread ID, and throw an exception if it is not the same as the original thread ID.
Essentially what this does is rolls your own single threaded apartment threading model. An "single threaded apartment" object is an object that can only be accessed legally on the thread which created it. (You buy a TV, you put it in your apartment, only people in your apartment are allowed to watch your TV.) The details of single threaded apartments vs multithreaded apartments vs free threading get quite complicated; see this question for more background.
Could you explain STA and MTA?
This is why, for instance, you must never access a UI element that you create on the UI thread from a worker thread; the UI elements are STA objects.