Windows 表单中的多线程

Multithreading in Windows Forms

我用我的模型算法(加密和解密)在 Windows Forms CLI 中用动态链接库(在 C++ 和 ASM 中)编写了应用程序 Caesar Cipher。我的应用程序的那部分正在运行。

这里还有一个来自Windows Forms的多线程。用户可以选择线程数 (1-64)。如果他选择 2,则要加密(解密)的消息将分为两个子字符串,这两个子字符串将分为两个线程。我想并行执行这些线程,最终减少执行时间成本。

当用户按下加密或解密按钮时,将显示加密或解密的文本和执行 C++ 和 ASM 函数的时间成本。其实一切都很好,但是大于1的线程的时间不是更小,而是更大。

有一些代码:

/*Function which concats string for substrings to threads*/
        array<String^>^ ThreadEncipherFuncCpp(int nThreads, string str2){
            //Tablica wątków
            array<String^>^ arrayOfThreads = gcnew array <String^>(nThreads);
            //Przechowuje n-tą część wiadomosci do przetworzenia 
            string loopSubstring;
            //Długość podstringa w wiadomości
            int numberOfSubstring = str2.length() / nThreads;
            int isModulo = str2.length() % nThreads;
            array<Thread^>^ xThread = gcnew array < Thread^ >(nThreads);

            for (int i = 0; i < nThreads; i++)
            {
                if (i == 0 && numberOfSubstring != 0)
                    loopSubstring = str2.substr(0, numberOfSubstring);
                else if ((i == nThreads - 1) && numberOfSubstring != 0){
                    if (isModulo != 0)
                        loopSubstring = str2.substr(numberOfSubstring*i, numberOfSubstring + isModulo);
                    else
                        loopSubstring = str2.substr(numberOfSubstring*i, numberOfSubstring);
                }
                else if (numberOfSubstring == 0){
                    loopSubstring = str2.substr(0, isModulo);
                    i = nThreads - 1;
                }
                else
                    loopSubstring = str2.substr(numberOfSubstring*i, numberOfSubstring);

                ThreadExample::inputString = gcnew String(loopSubstring.c_str());
                xThread[i] = gcnew Thread(gcnew ThreadStart(&ThreadExample::ThreadEncipher));
                xThread[i]->Start();
                xThread[i]->Join();
                arrayOfThreads[i] = ThreadExample::outputString;
            }
            return arrayOfThreads;
        }}

这里是一个片段,负责计算C++的时间:

    /*****************C++***************/
    auto start = chrono::high_resolution_clock::now();
    array<String^>^ arrayOfThreads = ThreadEncipherFuncCpp(nThreads, str2);
    auto elapsed = chrono::high_resolution_clock::now() - start;

    long long milliseconds = chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
    double micro = milliseconds;
    this->label4->Text = Convert::ToString(micro + " microseconds");
    String^ str3;
    String^ str4;
    str4 = str3->Concat(arrayOfThreads);
    this->textBox2->Text = str4;
    /**********************************/

以及工作示例: 对于输入数据:"Some example text. Some example text2." 程序会显示:"Vrph hadpsoh whaw. Vrph hadpsoh whaw2."

1个线程的执行次数:

C++ time: 31231us. 
Asm time: 31212us.

2个线程的执行次数:

C++ time: 62488us. 
Asm time: 62505us.

4个线程的执行次数:

C++ time: 140254us. 
Asm time: 124587us.

32个线程的执行次数:

C++ time: 1002548us. 
Asm time: 1000020us.

如何解决这个问题?

它没有更快的原因是你没有让你的线程 运行 并行。

xThread[i] = gcnew Thread(gcnew ThreadStart(&ThreadExample::ThreadEncipher));
xThread[i]->Start();
xThread[i]->Join();

这三行创建线程,启动它运行ning,然后等待它完成。你在这里没有得到任何并行性,你只是增加了生成和等待线程的开销。

如果你想从多线程中获得加速,方法是一次启动所有线程,让它们全部运行,然后收集结果。

在这种情况下,我会这样做 ThreadEncipher(您没有向我们展示其来源,所以我在做假设)采用一个参数,该参数用作数组指数。不是让 ThreadEncipherinputString 读取并写入 outputString,而是让它读取并写入数组的一个索引。这样,每个线程都可以同时读写。在生成所有这些线程之后,您可以等待所有这些线程完成,然后您可以处理输出数组,或者由于 array<String^>^ 已经是您的 return 类型,只需 return 照原样。

其他想法:

  • 您在这里混合了非托管和托管对象。如果你选择一个并坚持下去会更好。由于您使用的是 C++/CLI,我建议您坚持使用托管对象。我会停止使用 std::string,而只使用 System::String^
  • 由于您的 CPU 有 4 个核心,您不会通过使用超过 4 个线程来获得任何加速。当 32 个线程花费的时间比 4 个长时,请不要感到惊讶,因为您正在执行 8 倍的字符串操作,并且您有 32 个线程争夺 4 个处理器内核。
  • 您的字符串拆分代码比需要的更复杂。你那里有五个不同的案例,我必须坐下来考虑一下,以确保它是正确的。试试这个:

    int totalLen = str2->length;
    for (int i = 0; i < nThreads; i++)
    {
        int startIndex = totalLen * i / nThreads;
        int endIndex = totalLen * (i+1) / nThreads;
        int substrLen = endIndex - startIndex;
    
        String^ substr = str2->SubString(startIndex, substrLen);
        ...
    }