【问题标题】:How do you put an object in another thread?你如何将一个对象放在另一个线程中?
【发布时间】:2021-10-26 23:04:17
【问题描述】:

在 C# 中有什么方法可以将对象放入另一个线程中?我发现的只是如何在另一个线程中实际执行一些方法。我真正想做的是在一个新线程中实例化一个对象,以便以后使用它提供的方法。

希望你能帮助我, 罗素

【问题讨论】:

  • 创建静态对象
  • facepalm @pranay :)
  • 使用 ParameterizedThreadStart 将对象传递给您的线程。

标签: c# multithreading


【解决方案1】:

对象并不真正属于线程。如果你有一个对象的引用,你可以从许多线程中访问它。

这可能会导致无法从多个线程访问的对象(例如(几乎所有)System.Windows.Forms 类)以及对 COM 对象的访问出现问题。

如果您只想从同一个线程访问一个对象,请将对该线程的引用存储在对象(或包装对象)中,并通过该线程执行方法。

【讨论】:

    【解决方案2】:

    关于线程如何在这里工作似乎有些混乱,所以这是一个入门读物(也很短,所以你应该先找到更多的材料,然后再进一步涉足多线程编程。)

    对象和内存本质上是多线程的,因为进程中的所有线程都可以选择访问它们。

    所以对象与线程没有任何关系。

    但是,代码是在线程中执行的,而您可能要在其中执行代码的线程。

    不幸的是,没有办法像您所说的那样“将对象放入不同的线程”,您需要专门启动一个线程并指定要在该线程中执行的代码。该代码使用的对象因此可以被“说”为属于该线程,尽管这是您自己强加的人为限制。

    所以没有办法做到这一点:

    SomeObject obj = new SomeObject();
    obj.PutInThread(thatOtherThread);
    obj.Method(); // this now executes in that other thread
    

    事实上,许多新的多线程程序员陷入的一个常见陷阱是,如果他们在一个线程中创建一个对象,然后从另一个线程调用它的方法,那么所有这些方法都会在创建该对象的线程中执行。这是不正确的,方法总是在调用它们的线程中执行。

    所以下面的也是不正确的:

    Thread 1:
        SomeObject obj = new SomeObject();
    
    Thread 2:
        obj.Method(); // executes in Thread 1
    

    这里的方法将在线程2中执行。要让方法在原始线程中执行的唯一方法是与原始线程合作并“请求它”执行该方法。如何做到这一点取决于具体情况,有很多方法可以做到这一点。

    总结一下你想要什么:你想创建一个新线程,并在该线程中执行代码。

    为此,请查看 .NET 的 Thread 类。

    但请注意:多线程应用程序非常难以正确处理,我不会在程序中添加多线程功能,除非:

    1. 这是获得更高性能的唯一方法
    2. 而且,你知道你在做什么

    【讨论】:

      【解决方案3】:

      一个进程的所有线程共享相同的数据(忽略线程本地存储),因此无需在线程之间显式迁移对象。

      internal sealed class Foo
      {
          private Object bar = null;
      
          private void CreateBarOnNewThread()
          {
              var thread = new Thread(this.CreateBar);
      
              thread.Start();
      
              // Do other stuff while the new thread
              // creates our bar.
              Console.WriteLine("Doing crazy stuff.");
      
              // Wait for the other thread to finish.
              thread.Join();
      
              // Use this.bar here...
          }
      
          private void CreateBar()
          {
              // Creating a bar takes a long time.
              Thread.Sleep(1000);            
      
              this.bar = new Object();
          }
      }
      

      【讨论】:

        【解决方案4】:

        所有线程都可以看到 stack heap,所以如果线程引用了您需要的对象(例如通过方法传入),那么线程可以使用这些对象。这就是为什么在多线程时访​​问对象必须非常小心,因为两个线程可能会同时尝试更改对象。

        .NET 中有一个 ThreadLocal<T> 类,可用于将变量限制到特定线程:请参阅 http://msdn.microsoft.com/en-us/library/dd642243.aspxhttp://www.c-sharpcorner.com/UploadFile/ddoedens/UseThreadLocals11212005053901AM/UseThreadLocals.aspx

        【讨论】:

        • “所有线程都可以看到堆栈”:不...每个线程都可以看到它的自己的堆栈。但是,所有线程都可以看到堆。
        • 我的错误。混淆堆和栈——这是初学者的错误! :(
        【解决方案5】:

        使用ParameterizedThreadStart 将对象传递给您的线程。

        【讨论】:

          【解决方案6】:

          关于线程有很多行话,但归结起来很简单。

          对于一个简单的程序,您有一个从点 a 到 b 的执行点,一次一行。编程 101 对吧?

          好的,对于多线程,现在您的程序中有多个执行点。所以,第 1 点可以在程序的某个部分,第 2 点可以在其他地方。

          它们都是相同的内存、数据和代码,但您一次会发生不止一件事。所以,你可以想,两个点同时进入一个循环会发生什么,你认为会发生什么?因此,人们创造了一些技术来防止这类问题发生,或者加速某种过程。 (计算价值与网络。)

          实际上就是这样。管理起来可能很棘手,而且很容易迷失在行话和理论中,但请记住这一点,它会简单得多。

          与往常一样,该规则还有其他例外,但这是它的基础。

          【讨论】:

            【解决方案7】:

            如果您在线程中运行的方法驻留在自定义类中,您可以让该类的成员来保存参数。

            public class Foo
            {
               object parameter1;
               object parameter2;
            
               public void ThreadMethod()
               {
                   ...
               }
            }
            

            【讨论】:

            • 我想知道你为什么被否决。您的方法干净且易于理解。主线程将通过线程启动的对象访问新创建的对象。同时您的线程可以创建对象而不会干扰其他线程。
            • 我也想知道:)
            • 我觉得这个方案太天真了。对象不属于线程。对象是独立存在的实体。 IMO,如果需要像 Foo 这样的类,则存在设计错误。
            【解决方案8】:

            “供以后使用它提供的方法。”

            使用包含在新线程上执行的方法和其他数据和方法的类,您可以从您的线程访问来自新线程的数据和方法。

            但是......如果你从类中执行一个方法,你正在当前线程上执行。

            要在新线程上执行方法需要一些线程同步。

            System.Windows.Forms.Control.BeginInvoke 这样做,Control 线程一直在等待,直到请求到达。

            WaitHandle 类可以帮助您。

            【讨论】:

              【解决方案9】:

              抱歉重复了一些以前的工作,但是 OP 说

              我真正想做的是在一个新线程中实例化一个对象,以便以后使用它提供的方法。

              让我解释为:

              我真正想做的是让一个新线程实例化一个对象,以便稍后我可以使用该对象的方法。

              如果我错过了分数,请纠正我。示例如下:

              namespace  silly
              {
                  public static class Program
                  {
                      //declared volatile to make sure the object is in a consistent state
                      //between thread usages -- For thread safety.
                      public static volatile Object_w_Methods _method_provider = null;
                      static void Main(string[] args)
                      {
                          //right now, _method_provider is null.
                          System.Threading.Thread _creator_thread = new System.Threading.Thread(
                              new System.Threading.ThreadStart(Create_Object));
                          _creator_thread.Name = "Thread for creation of object";
                          _creator_thread.Start();
              
                          //here I can do other work while _method_provider is created.
                          System.Threading.Thread.Sleep(256);
              
                          _creator_thread.Join();
              
                          //by now, the other thread has created the _method_provider
                          //so we can use his methods in this thread, and any other thread!
              
                          System.Console.WriteLine("I got the name!!  It is: `" + 
                              _method_provider.Get_Name(1) + "'");
              
                          System.Console.WriteLine("Press any key to exit...");
                          System.Console.ReadKey(true);
              
                      }
                      static void Create_Object()
                      {
                          System.Threading.Thread.Sleep(512);
                          _method_provider = new Object_w_Methods();
                      }
                  }
                  public class Object_w_Methods
                  {
                      //Synchronize because it will probably be used by multiple threads,
                      //even though the current implementation is thread safe.
                      [System.Runtime.CompilerServices.MethodImpl( 
                          System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
                      public string Get_Name(int id)
                      {
                          switch (id)
                          {
                              case 1:
                                  return "one is the name";
                              case 2:
                                  return "two is the one you want";
                              default:
                                  return "supply the correct ID.";
              }}}}
              

              【讨论】:

                【解决方案10】:

                就像详细说明以前的答案。回到问题,对象和内存空间由所有线程共享。所以它们总是共享的,但我假设你想安全地这样做并使用另一个线程创建的结果。

                首先尝试一种受信任的 C# 模式。 Async Patterns 有一些设置模式可以使用,它们确实在线程之间传输基本消息和数据。 通常一个威胁在计算结果后完成!

                生命威胁:异步和共享生命威胁数据时,没有什么是万无一失的。 因此,如果您确实需要走这条路线并尝试遵循已知模式,请尽量保持简单。

                所以现在我只想详细说明为什么一些已知模式具有一定的结构:

                Eventargs:在传递对象之前创建对象的深层副本。 (这不是万无一失的,因为某些参考文献可能仍被共享。) 使用 int 浮点数等基本类型传递结果,这些可以在构造函数上创建并使其不可变。

                这些类型的原子关键字之一,或创建监视器等。坚持一个线程读取另一个写入。

                假设你有一个复杂的数据,你喜欢同时在两个线程上使用一个完全不同的方法来解决这个问题,我还没有测试过: 您可以将结果存储在数据库中并让其他可执行文件读取它。 (有锁发生在行级别,但你可以再试一次或更改 SQL 代码,至少你会得到报告的死锁,可以通过良好的设计解决,而不仅仅是挂起软件!!)我只会这样做,如果它真的使出于其他原因将数据存储在数据库中的意义。

                另一种有用的方法是编写 F# 。默认情况下,对象和所有类型都是不可变的/因此您要共享的对象应该有一个构造函数,并且没有方法允许更改对象或增加基本类型。 所以你创造了它们,然后它们就不会改变!所以在那之后它们是不可变的。 使锁定它们并并行处理它们变得更加容易。不要在 C# 类中对此感到疯狂,因为其他人可能会遵循这个“约定”,并且大多数像 Lists 这样的东西在 C# 中并不是设计为不可变的( readonly 与不可变的不同,const 是但它非常有限)。 Immutable versus readonly

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多