如何在 main 之前预加载数据

Posted

技术标签:

【中文标题】如何在 main 之前预加载数据【英文标题】:How to preload data before main 【发布时间】:2013-02-21 22:47:58 【问题描述】:

我的目标是快速处理各种竞争条件,如果在大约同一时间在 2 个单独的线程中调用给定函数时可能会导致问题。我的快速修复是通过在 main() 之前调用它们来保证函数已经被初始化。这是我想出的解决方案,但我觉得我可能会重新发明***。 MSVC2010 STL 中是否有可用的选项? (还没有提升)或者是否有更好的方法来快速处理这些问题,而无需在这种情况下为每个函数添加重要的线程安全代码?

template <typename T, T func>
struct PreLoaderHelper

    PreLoaderHelper()
    
        wcout << L"Preload helper constructor" << endl;
        func();
    
;

template <typename T, T func>
struct PreLoader

    static PreLoaderHelper<T, func> _helper;
;

template <typename T, T func>
PreLoaderHelper<T, func> PreLoader<T, func>::_helper;

#define PRELOAD(fn) template struct PreLoader<decltype(&fn), fn>; 

void foo()  wcout << L"inside foo" << endl; 
void bar()  wcout << L"inside bar" << endl; 
vector<wstring> & foobar() 
 
    static vector<wstring> sPresidents;
    if(sPresidents.size() == 0)
    
        sPresidents.push_back(L"George Washington");
        sPresidents.push_back(L"John Addams");
        sPresidents.push_back(L"Thomas Jefferson");
    
    return sPresidents;

wstring foofoo(const wstring &)  wcout << L"inside foofoo" << endl; return L"foofoo";

PRELOAD(foo);
PRELOAD(bar);
PRELOAD(foobar);
PRELOAD(foo);

int main()

    return 0;

【问题讨论】:

编程真的很棒,每个人的想法完全不同。你能解释一下为什么不使用互斥锁吗? MSVC2010 是否支持std::call_once?但是,如果我看到并发代码访问相同数据而不使用互斥锁或原子,我总是会怀疑。此外,我们可以看到您的解决方案,但看不到您的实际问题。你的解决方案最好在codereview.stackexchange.com。 速度主要是写和执行。创建大量互斥锁(每个函数一个)并为每个互斥锁添加锁定将花费我更长的时间。此外,通过在每次调用特定函数时锁定互斥锁,执行时间会显着延长。我可以通过双重检查锁定实现来解决这个问题,但是要为我选择的平台正确实现每个功能需要付出更多的努力。 @Zeta,我会更新一点 是否会调用PRELOAD 避免给定函数大约在同一时间在 2 个单独的线程中调用 @KenKin 是的 - 这会导致函数作为全局(静态)对象的构造函数的一部分被调用一次;由于此类对象是在 main 之前初始化的,因此它应该保证我们不会遇到竞争条件,除非某个地方的另一个类似构造以相同的方式启动线程。 【参考方案1】:

第一个问题:你之前真的要打电话给他们吗 进入主线?为什么不把它们称为 main 中的第一件事, 在开始任何线程之前?

否则:经典的习惯用法是在初始化器中使用它们 到静态变量。通常的方法是从 构造函数;如果您有额外的数据必须是 初始化,这无疑是最好的方法。如果不, 像这样简单的东西:

static bool initialized = (function(), true);

会成功的。

形式上,这仅保证它们将被初始化 在使用同一翻译单元中的任何其他内容之前, 但实际上,这将保证调用该函数 在 main 之前,或者在加载 DLL 期间,如果它在 DLL 中 除了带有 main 的那个。

【讨论】:

是的,这基本上就是我想出的,但在某种程度上我不需要用一堆额外的变量名称污染任何命名空间(或者期望一个全局的“初始化”变量外部定义)。至于为什么不调用 main,我喜欢将此逻辑保留在函数旁边的想法,因此稍后查看代码的人不会想知道“如果我的 2 个线程尝试一起初始化怎么办?” @Rollie 如果您真的担心会污染某些命名空间,请为每次初始化创建一个特殊的命名空间。 (namespace STRCONCAT( ForInitialization, __LINE__ ) 在匿名命名空间中,如果你真的很偏执的话。)恕我直言,将其设为静态或将其置于匿名命名空间中就足够了。 @Rollie 最后:这种初始化通常与特定的类有关。如果是这样,使用类的私有静态成员变量将使名称尽可能保持私有。 @LokiAstari 你无法防范所有可能的愚蠢行为。 他们总是会制造更好的白痴。我考虑了 STRCONCAT 解决方案,我倾向于回避这类事情。此外,模板解决方案可防止同一函数的多次初始化。不过,你的答案很好【参考方案2】:

你可以这样做:

int dummy = (foo(), (void)0, bar(), 0);

int main() 

    // foo() and bar() have already been called

此外,C++11 保证以下变体仅导致一次调用,无竞争:

void call_foo()

    static int dummy = (call_foo(), 0);


void some_thread_function()  call_foo(); 

【讨论】:

哦,很好地使用了逗号运算符。 VC10 没有我想的那么合规,所以我认为你的第二部分不会起作用。【参考方案3】:

如果您使用的是 C++11,请记住:

    静态函数变量初始化是线程安全的。 您可以使用列表初始化语义。

试试:

std::vector<std::wstring>& plop1()

    // Thread safe
    // Guaranteed to be done once
    // No setup required
    // No cost if not used.
    static std::vector<std::wstring> sPresidents =
       
        L"George Washington",
        L"John Addams",
        L"Thomas Jefferson"
    ;  
    return sPresidents;

【讨论】:

哦,我多么希望这是可能的...尝试了几次战斗,但没有成功。而且 VC11 在最新的补丁中添加了很多其他不错的功能【参考方案4】:

我强烈建议根据您的情况使用关键部分进行适当的同步。进入和退出临界区不会添加很多代码,并且可以优雅地处理这种情况。

如果您不想继续在 main() 之前初始化函数的原始方法,您可以使用全局变量初始化,因为它发生在 main 函数调用之前。在

上有一篇关于这种方法的好文章

http://blog.fishingcactus.com/index.php/2009/01/28/fixing-c-static-and-global-variable-initialization/#sthash.pmBtrYD8.dpbs

【讨论】:

以上是关于如何在 main 之前预加载数据的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 swift 3 xcode 8 在核心数据中预加载数据库

如何以静默方式预加载有声音的影片剪辑?

如何在控制器之前加载.run

iis的预加载已启用要开吗?

iOS:如何在显示前预加载数据

如何在预加载的数据库中填充 ListView