gpt4 book ai didi

c++ - 线程静态类方法与全局范围

转载 作者:行者123 更新时间:2023-11-30 03:03:31 25 4
gpt4 key购买 nike

想象一下一个应用程序的功能,该功能需要多达5个线程来处理数据,这些线程使用缓冲区,互斥量和事件进行交互。性能至关重要,语言是C++。

该功能可以实现为具有一个类的一个(编译)单元,并且只能为该应用实例化该类的一个实例。该类本身在run()方法中实现线程中的1个,它会在用户关闭应用程序时生成其他4个线程,对其进行管理并收集它们。

选择以下一种方法而不是另一种方法有什么好处(请让我知道更好的方法)?

  • 向类添加5个静态方法,每个方法运行一个线程,互斥体和其他作为静态类变量共享的数据。
  • 添加5个全局函数(无作用域)并使用全局变量,事件和互斥锁(好像是C)
  • 完全更改模式,添加4个以上的类,每个类实现一个线程,并通过全局变量共享数据。

  • 以下是一些需要考虑的想法和问题(如果错误,请更正它们):
  • 将线程作为类成员(当然是静态的),他们可以依靠单例来访问非静态的成员函数,它还为它们提供了一个命名空间,这本身就是一个好主意。
  • 使用静态类方法,类头文件很快将包含许多静态变量(以及其他辅助静态方法)。必须在类头文件中声明变量可能会给包含头文件的其他单元带来额外的依赖性。如果全局声明了变量,则可以将它们隐藏在单独的头文件中。
  • 静态类变量应该在代码中的某个位置定义,因此它使键入声明的内容加倍。
  • 编译器可以利用 namespace 解析来获得更优化的代码(与可能以不同单位表示的全局变量相反)。
  • 可能会更好地优化单个单元,而整个程序的优化速度很慢,并且效果可能较差。
  • 如果单元增加,我必须将部分代码移到一个单独的单元上,因此我将拥有一个包含多个(编译)单元的类,这是否是反模式?
  • 如果使用多个类,每个类都处理一个线程,则可以再次提出相同的问题,以在实现线程的静态方法和全局函数之间做出决定。另外,这需要更多的代码留置权,这不是一个真正的问题,但这是否值得额外的开销呢?

  • 请假设没有Qt之类的库,然后假设我们可以依赖QThread并为每个run()方法实现一个线程,请回答此问题。

    Edit1:每个设计的线程数是固定的,数字5仅是示例。请分享您对方法/模式的看法,而不是对细节的看法。

    Edit2:我发现 this answer(对于另一个问题)非常有帮助,我猜第一种方法将类误用作 namespace 。如果与 namespace 结合使用,则可以减少第二种方法。

    最佳答案

    资料来源
    首先,您应该阅读Herb Sutter的全部并发文章:
    http://herbsutter.com/2010/09/24/effective-concurrency-know-when-to-use-an-active-object-instead-of-a-mutex/
    这是上一篇文章的链接,其中包含所有以前文章的链接。
    你怎么了
    根据以下文章:您具有或需要多少可伸缩性? (http://drdobbs.com/parallel/201202924),您处于O(K): Fixed情况下。也就是说,您有一组要同时执行的固定任务。
    根据应用程序的描述,您有5个线程,每个线程做的事情都非常不同,因此您必须有5个线程,也许希望其中的一个或多个线程仍可以将其任务划分为多个线程(因此,使用一个线程池),但这将是一个奖励。
    我让您阅读该文章以获取更多信息。
    设计问题
    关于单例
    忘记单例。这是一个dumb, overused pattern
    如果您真的想限制类实例的数量(并且说真的,您做得更好吗?),您应该将设计分为两部分:一个用于数据的类,另一个用于包装的类前一类进入单例限制。
    关于编译单元
    使标题和资源易于阅读。如果您需要将一个类的实现分为多个源,那就这样吧。我据此命名来源。例如,对于MyClass类,我将有:

  • MyClass.hpp:头
  • MyClass.cpp:主要来源(带有构造函数等)
  • MyClass.Something.cpp:带有
  • 的源代码处理
  • MyClass.SomethingElse.cpp:使用其他
  • 进行源代码处理
  • 等。

  • 关于编译器优化
    最近的编译器能够内联来自不同编译单元的代码(我在Visual C++ 2008,IIRC上看到了该选项)。我不知道整个全局优化的效果是否比“一个单元”的编译效果差,但是即使是这样,您仍然可以将代码分成多个源,然后让一个全局源包含所有内容。例如:
  • MyClassA.header.hpp
  • MyClassB.header.hpp
  • MyClassA.source.hpp
  • MyClassB.source.hpp
  • global.cpp

  • 然后相应地进行包含。但是,您应该确保这样做实际上可以提高性能:除非真正需要它并且对其进行了概要分析,否则请不要进行优化。
    您的情况,但更好吗?
    您的问题和评论所涉及的是整体设计,而不是性能或线程问题,所以我可能是错的,但是您需要的是简单的重构。
    我将使用第三种方法(每个线程一个类),因为类带有私有(private)/公共(public)访问权限,因此,您只能通过将其设为私有(private)来使用它来保护一个线程拥有的数据。
    以下准则可以为您提供帮助:
    1-每个线程应隐藏在一个非静态对象中
    您可以使用该类的私有(private)静态方法,也可以使用匿名命名空间的函数(我会使用该函数,但是在这里,我想访问该类的私有(private)函数,因此我将使用该静态方法) 。
    通常,线程构造函数使您可以将指针传递给具有 void *上下文参数的函数,因此可使用该函数将 this指针传递给主线程函数:
    每个线程只有一个类,可以帮助您隔离该线程,从而使该线程的数据与外界隔离:没有其他线程将能够访问该数据,因为它是私有(private)的。
    这是一些代码:
    // Some fictious thread API
    typedef void (*MainThreadFunction)(void * p_context) ;
    ThreadHandle CreateSomeThread(MainThreadFunction p_function, void * p_context) ;

    // class header
    class MyClass
    {
    public :
    MyClass() ;
    // etc.

    void run() ;

    private :
    ThreadHandle m_handle ;

    static void threadMainStatic(void * p_context) ;
    void threadMain() ;
    }
    // source
    void MyClass::run()
    {
    this->m_handle = CreateSomeThread(&MyClass::threadMainStatic, this) ;
    }

    void MyClass::threadMainStatic(void * p_context)
    {
    static_cast<MyClass *>(p_context)->threadMain() ;
    }

    void MyClass::threadMain()
    {
    // Do the work
    }
    Displaimer:尚未在编译器中测试过。将其视为伪C++代码,而不是实际代码。 YMMV。
    2-标识未共享的数据。
    可以将这些数据隐藏在拥有对象的私有(private)部分中,如果通过同步对其进行保护,则这种保护是过大的(因为未共享数据)
    3-识别共享的数据
    ...并验证其同步性(锁定,原子访问)
    4-每个类应具有自己的标题和源
    ...并在必要时通过同步保护对其(共享)数据的访问
    5-尽可能保护访问
    如果一个函数仅由一个类使用,并且仅一个类使用,并且实际上不需要访问该类的内部,则可以将其隐藏在匿名 namespace 中。
    如果一个变量仅由一个线程拥有,请将其作为私有(private)变量成员隐藏在类中。
    等等。

    关于c++ - 线程静态类方法与全局范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9321111/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com