为什么可以在同一类中创建的另一个线程中访问局部变量? [英] Why can a local variable be accessed in another thread created in the same class?

查看:109
本文介绍了为什么可以在同一类中创建的另一个线程中访问局部变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这个确切的主题上我什么都找不到,所以,如果已经存在问题,请引导我朝正确的方向前进.

根据我对.NET的了解,不可能跨不同的线程访问变量(如果该语句有误,请更正我,这只是我在某处阅读的内容).

但是,现在在此代码示例中,它似乎不应该工作:

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();
    }
}

当我运行SomeMethod()时,.NET不应引发异常,因为创建的对象mc在与mc -initializer中创建的线程不同的线程中运行,并且此新线程正在尝试访问mc的局部变量?

MessageBox10显示为(不是)预期的,但是我不确定为什么这应该起作用.

也许我不知道要搜索什么,但找不到我可以解决的线程主题,但是也许我对变量和线程的想法是错误的.

解决方案

根据我对.NET的了解,不可能跨不同的线程访问变量.如果该说法有误,请纠正我,这只是我在某处阅读的内容.

那句话完全是错误的,所以请考虑一下.

您可能在某个地方读到了局部变量,该变量不能在不同的线程之间访问.该声明也是 错误,但通常会被声明.正确的说法是局部变量不是

  • 采用异步方法
  • 在迭代器块(即具有yield returnyield break的方法)中
  • 匿名函数的封闭外部变量

不能被多个线程访问.甚至那个说法有点狡猾.有多种方法可以使用指针和unsafe代码块,但是尝试这样做是一个非常糟糕的主意.

我还注意到您的问题询问局部变量,但随后给出了 field 的示例.根据定义,字段不是是局部变量.根据定义,局部变量在方法主体中是 local . (或构造函数主体,索引器主体等),请确保您对此没有任何疑问.本地的定义特征不是它在堆栈上"或类似的东西.本地的本地"部分是其名称在方法主体之外没有意义.

在一般情况下:变量是存储位置,它引用内存.线程是进程中的控制点,并且进程中的所有线程共享相同的内存.这就是使它们成为 threads 而不是 processes 的原因.因此,通常来说,所有变量都可以在任何时间,以所有顺序被多个线程访问,除非有某种机制可以防止这种情况发生,否则.

让我再说一遍,只是为了确保它绝对清晰:考虑单线程程序的正确方法是,所有变量都是 stable ,除非有所改变. .考虑多线程程序的正确方法是,所有变量都以没有特定的顺序不断地变异,除非某些因素使它们保持静止或井然有序. 这是为什么多线程共享内存模型如此困难的根本原因,因此您应该避免使用它.

在您的特定示例中,两个线程都可以访问this,因此,两个线程都可以看到变量this.variable.您尚未实现防止这种情况的机制,因此两个线程都可以以任何顺序写入和读取该变量,而实际上受的约束很少.您可能已实施以驯服此行为的一些机制是:

  • 将变量标记为ThreadStatic.这样做会在每个线程上创建一个新变量.
  • 将变量标记为volatile.这样做对如何观察到读写的顺序施加了某些限制,并且对编译器或CPU进行的优化可能会导致意外结果的施加了某些限制.
  • 在变量的每次用法周围放置一个lock语句.
  • 首先不要共享变量.

除非您对多线程和处理器优化有的理解,否则我建议不要使用任何选项,但后者除外.

现在,假设您确实希望确保在另一个线程上对变量的访问失败.您可以让构造函数捕获创建线程的线程ID并将其存储起来.然后,您可以通过属性getter/setter访问该变量,在该属性中,getter和setter检查当前线程ID,如果与原始线程ID不同,则引发异常.

实质上,这是运行您自己的单线程单元线程模型. 单线程单元"对象是只能在创建它的线程上合法访问的对象. (您购买一台电视,然后将其放在您的公寓中,只有公寓中的人才可以观看您的电视.)单线程公寓与多线程公寓与免费线程的详细信息变得非常复杂.有关更多背景信息,请参见此问题.

您能解释一下STA和MTA吗?

例如,这就是为什么您永远不能从工作线程访问在UI线程上创建的UI元素的原因; UI元素是STA对象.

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

  • in an async method
  • in an iterator block (that is, a method with a yield return or yield break)
  • closed-over outer variables of an anonymous function

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:

  • Mark the variable as ThreadStatic. Doing so causes a new variable to be created on each thread.
  • Mark the variable as 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.
  • Put a lock statement around every usage of the variable.
  • Don't share the variable in the first place.

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.

这篇关于为什么可以在同一类中创建的另一个线程中访问局部变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆