第23講 BackgroundWorkerによるマルチスレッド
第5話 BackgroundWorker版素数探索ソフト解答例

#pragma once
#include <math.h>
#include<stdlib.h>
#include<set>
using namespace std;
int N;
int cn1,cn2,cn3,cn4;
int s1[25000000],s2[25000000],s3[25000000],s4[25000000];
namespace BackgroundWorkerによる素数探索 {
      ・
      ・
      ・
#pragma endregion
   private: System::Void button1_Click_1(System::Object^ sender, System::EventArgs^ e) {
           set<int> s;
           int i,j,cn;
           N=int::Parse(textBox1->Text);
           s.clear();
           s.insert(2);s.insert(3);s.insert(5);
           for(i=0;i<cn1;i++){
             s1[i]=0;
             cn1=0;
           }
           for(i=0;i<cn2;i++){
             s2[i]=0;
             cn2=0;
           }
           for(i=0;i<cn3;i++){
             s3[i]=0;
             cn3=0;
           }
           for(i=0;i<cn4;i++){
             s4[i]=0;
             cn4=0;
           }
           DateTime^ hj=DateTime::Now; //開始時間

           backgroundWorker1->RunWorkerAsync();
           backgroundWorker2->RunWorkerAsync();
           backgroundWorker3->RunWorkerAsync();
           backgroundWorker4->RunWorkerAsync();

           array<String^>^ w=gcnew array<String^>(10);
           DateTime^ ow=DateTime::Now; //終了時間

           
i=0;
           while(1){                 //無駄打ち
             i++;
             if(i==100000000)break;
           }

           
i=0;
           while(1){
             s.insert(s1[i]);

             i++;
             if(cn1<i)break;
           }
           i=0;
           while(1){
             s.insert(s2[i]);
             i++;
             if(cn2<i)break;
            }
            i=0;
            while(1){
              s.insert(s3[i]);
              i++;
              if(cn3<i)break;
            }
            i=0;
            while(1){
              s.insert(s4[i]);
              i++;
              if(cn4<i)break;
           }
           cn=0;
           for(set<int>::iterator it=s.begin();it!=s.end();it++){
             if(*it==0)it++;
             w[cn%10]=(*it).ToString();
             if(cn==cn1+cn2+cn3+cn4+2){
             for(j=(cn%10)+1;j<10;j++){
               w[j]=L"";
             }
           }
           if(cn%10==9)dataGridView1->Rows->Add(w);
           cn++;
         }
         dataGridView1->Rows->Add(w);

         TimeSpan sa=ow->Subtract(*hj); //経過時間の計算
         w[0]=L"計算時間"; w[1]=sa.TotalSeconds.ToString();
         for(i=2;i<10;i++)w[i]=L"";
         dataGridView1->Rows->Add(w);
         w[0]=L"素数個数"; w[1]=(cn).ToString();
         dataGridView1->Rows->Add(w);
         w[0]=L"1余る";w[1]=cn1.ToString();w[2]=L"2余る";w[3]=cn2.ToString();w[4]=L"3余る";w[5]=cn3.ToString();w[6]=L"3余る";w[7]=cn4.ToString();
         dataGridView1->Rows->Add(w);
         int min;
         min=cn1;
         if(cn2<min)min=cn2;
         if(cn3<min)min=cn3;
         if(cn4<min)min=cn4;
         w[0]=L"1余る";w[1]=(((double)cn1/(double)min)).ToString();w[2]=L"2余る";
         w[3]=(((double)cn2/(double)min)).ToString();w[4]=L"3余る";w[5]=(((double)cn3/(double)min)).ToString();w[6]=L"4余る";
         w[7]=(((double)cn4/(double)min)).ToString();
         dataGridView1->Rows->Add(w);
       }
private: System::Void backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
  for (int i=6; i<=N; i+=5) {
    if(sh(i)){
      s1[cn1]=i;
      cn1++;
    }
  }
}
private: System::Void backgroundWorker2_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
  for (int i=7; i<=N; i+=5) {
    if(sh(i)){
      s2[cn2]=i;
      cn2++;
    }
  }
}
private: System::Void backgroundWorker3_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
  for (int i=8; i<=N; i+=5) {
    if(sh(i)){
      s3[cn3]=i;
      cn3++;
    }
  }
}
private: System::Void backgroundWorker4_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
  for (int i=9; i<=N; i+=5) {
    if(sh(i)){
      s4[cn4]=i;
      cn4++;
    }
  }
}

  static char sh(double n){
    char h=1;
    double a;
    int b;
    int c;
    int i;

    a=sqrt(n);
    b=(int)a;
    c=(int)n;
    if(c % 2==0)h=0;
    if(h==1){
      for(i=3;i<=a;i=i+2){
        if(c % i==0){
          h=0;
          break;
        }
      }
    }
    return(h);
  }

};
}


           while(1){                 //無駄打ち
             i++;
             if(i==100000000)break;
           }

は無駄打ちです。これですべてのスレッドが終了するまでの時間稼ぎをしているわけです。


さて、実験結果です。

探索範囲 100 1000 10000 100000 1000000 10000000
マルチ4スレッド版 個数 25 168 1229 9592 78498 664579
時間 0.078 0.0312 0.0156 0.0156 0.1872 2.7144
マルチ4スレッドBackgroundWorker版 個数 25 168 1229 9592 78498 664579
時間 0 0 0 0 0 0.0468

処理時間は、backgroundWorker1のようが速いようです。

実は、よく実験してみると無駄打ちさせても探索範囲が大きくなると、
結果が安定しません。if(i==100000000)break;
の数字を大きくすれば安定しますが、芸がありません。
終了通知を使ってもっとすっきりしたものができないか、
試行錯誤しましたが、私はその方法を発見できませんでした。
うまい方法があったら、教えて頂ければと思います。

訂正(2011/03/06)
第25講の第5話をアップして、
           DateTime^ hj=DateTime::Now; //開始時間

           backgroundWorker1->RunWorkerAsync();
           backgroundWorker2->RunWorkerAsync();
           backgroundWorker3->RunWorkerAsync();
           backgroundWorker4->RunWorkerAsync();

           array<String^>^ w=gcnew array<String^>(10);
           DateTime^ ow=DateTime::Now; //終了時間
の 
           array<String^>^ w=gcnew array<String^>(10);
           DateTime^ ow=DateTime::Now; //終了時間

の部分は、全く意味がないことに気がつきました。
理由は、BackgroundWorkerは、

現在の直線的な流れから、スレッドを分岐させるものでした。
スレッドに分岐した後も水面上の本体のプログラムの方(上手の緑の線)は処理が進んでしまいます。
各派生スレッドは時間のかかる処理ですから、派生スレッドが処理しているうちに、
           array<String^>^ w=gcnew array<String^>(10);
           DateTime^ ow=DateTime::Now; //終了時間

の処理が実行されてしまいます。

皆様を混乱させてことをお詫び申し上げます。
VC++には、スレッドが終了するまで待機させる命令はないんでしょうか。
私には、わかりません。


第23講第4話へ 第24講第1話へ




VC++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座