代码之家  ›  专栏  ›  技术社区  ›  Krisc

静态代码如何在多线程中运行?

  •  13
  • Krisc  · 技术社区  · 16 年前

    我在看书 Threading from within a class with static and non-static methods

    static class Worker{
        public static MyObject DoWork(string filename){
            MyObject mo = new MyObject();
    
            // ... does some work
    
            return mo;
        }
    }
    

    我想采用这种方法,并在多线程的情况下使用它,这样我就可以一次读取多个文件。撇开设计问题/准则不谈,多线程如何访问此代码?

    假设我有这样的东西。。。

    class ThreadedWorker {
        public void Run() {
            Thread t = new Thread(OnRun);
            t.Start();
        }
    
        void OnRun() {
            MyObject mo = Worker.DoWork("somefilename");
    
            mo.WriteToConsole();
        }
    }
    

    6 回复  |  直到 9 年前
        1
  •  19
  •   Kevin Crowell    16 年前

    是的,该方法应该能够在多个线程中正常运行。您唯一应该担心的是同时在多个线程中访问同一个文件。

        2
  •  8
  •   Fredrik Mörk    16 年前

    在这种情况下,应该区分静态方法和静态字段。对静态方法的每个调用都有自己的方法及其局部变量的“副本”。这意味着在您的示例中,每个调用都将独立运行 MyObject 实例,调用之间将没有任何关系。这也意味着在不同的线程上执行它们没有问题。

        3
  •  3
  •   LBushkin    16 年前

    如果静态方法被写为线程安全的,那么它可以从任何线程调用,甚至可以传递到线程池。

    你必须记住-.NET对象没有 (位于线程堆栈上的结构除外)-执行路径不存在。因此,如果线程可以访问对象的实例,它就可以调用实例方法。任何线程都可以调用静态方法,因为它只需要知道对象的类型。

        4
  •  2
  •   Oliver Hanappi    16 年前

    在并发执行静态方法时,应该记住的一点是静态字段,它只存在一次。因此,如果该方法读取和写入静态字段,则可能会发生并发问题。

    但是,有一个属性称为 ThreadStaticAttribute 也就是说每个线程都有一个单独的字段。这在某些特定场景中可能会有所帮助。

    局部变量对于每个线程都是独立的,所以您不必关心这个问题。但是要注意像文件这样的外部资源,并发访问时可能会出现问题。

    顺致敬意,
    奥利弗·哈纳皮

        5
  •  2
  •   BenAlabaster    16 年前

    除了已经被回答的代码方面,您还需要考虑访问文件的I/O方面。

    设置一个布尔字段,称之为TerminateReaderThread。将其初始化为false,这将再次变得更加明显。

    设置队列<字符串>字段,将其称为文件并对其进行初始化。

    在将每个相关文件路径写入文件队列之前,我的主应用程序线程会检查文件队列上是否有锁。一旦文件被写入,reset事件就会触发,向队列读取器线程指示队列中存在未读文件。

    然后我设置一个线程作为队列读取器。这个线程使用WaitAny()方法等待ManualResetEvent被触发-这是一个阻塞方法,一旦ManualResetEvent被触发就会解除阻塞。一旦它被触发,线程将检查线程关闭是否已经启动[通过检查TerminateReaderThread字段]。如果启动了关闭,线程将正常关闭,否则它将从队列中读取下一项,并生成一个工作线程来处理该文件。然后我锁定队列,然后检查是否还有剩余的项目。如果没有留下任何项目,我将重置ManualResetEvent,它将在下一次遍历时暂停我们的线程。然后我解锁队列,以便主线程可以继续向其写入。

    工作线程的每个实例都会尝试在启动时使用的文件上获取独占锁,直到超时结束。如果锁定成功,它会处理该文件;如果锁定失败,它会根据需要重试,抛出异常并终止自身。在发生异常的情况下,线程可以将文件添加到队列的末尾,以便其他线程可以在稍后的时间再次提取该文件。请注意,如果您这样做,那么您需要考虑I/O读取问题可能导致的无休止循环。在这种情况下,带有失败次数计数器的失败文件字典可能很有用,这样,如果达到某个限制,您就可以停止将文件重新添加到队列末尾。

    一旦我的应用程序决定不再需要读取器线程,它就会将TerminateReaderThread字段设置为true。下次读卡器线程循环到其进程开始时,其关闭进程将被激活。

        6
  •  1
  •   i_am_jorf    16 年前

    再进入者 ,这意味着当从另一个线程(或更高的堆栈)执行已经在函数中时,执行可以安全地重新进入函数。

    在多个调用之间共享的唯一东西是控制台本身。如果您在多个线程上调用它,它们将相互输出到控制台。您可能会对同一个文件执行操作(在您的示例中,文件名是硬编码的),但您可能会对多个文件执行操作。如果先前的线程打开了文件,则后续线程可能无法打开该文件。

    推荐文章