且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

带线程矩阵乘法

更新时间:2022-06-20 21:42:48

这使用两个BackgroundWorker对象完成您想要的

This does what you want using two BackgroundWorker objects

public class MatrixCalc
{
    readonly double[,] a, b, c;
    readonly int a_rows, a_cols, b_rows, b_cols, c_rows, c_cols;
    bool result_ok;
    int thread_count;
    BackgroundWorker bw1, bw2;
    AutoResetEvent re;

    public MatrixCalc(double[,] a, double[,] b, double[,] c)
    {
        a_rows=a.GetLength(0);
        a_cols=a.GetLength(1);
        b_rows=b.GetLength(0);
        b_cols=b.GetLength(1);
        c_rows=c.GetLength(0);
        c_cols=c.GetLength(1);
        // keep references of arrays
        this.a=a;
        this.b=b;
        this.c=c;
    }

    public void Multiply()
    {
        result_ok=false;
        this.bw1=new BackgroundWorker();
        this.bw2=new BackgroundWorker();
        this.re=new AutoResetEvent(false);
        bw1.WorkerSupportsCancellation=true;
        bw1.DoWork+=new DoWorkEventHandler(bw_DoWork);            
        bw1.RunWorkerCompleted+=new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        bw1.RunWorkerAsync(0);
        bw2.WorkerSupportsCancellation=true;
        bw2.DoWork+=new DoWorkEventHandler(bw_DoWork);
        bw2.RunWorkerCompleted+=new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        bw2.RunWorkerAsync(1);
        re.WaitOne();
        re.WaitOne();
    }
    public bool OK { get { return result_ok; } }
    public void Cancel()
    {
        bw1.CancelAsync();
        bw2.CancelAsync();
        re.WaitOne();
        re.WaitOne();
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        thread_count--;
        this.result_ok=(!e.Cancelled)&&(thread_count==0);
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        thread_count++;
        if(!e.Cancel)
        {
            var offset=(int)e.Argument;
            for(int i=0; i<a_rows; ++i)
            {
                // This is the trick. Start from column 0 or 1
                // and skip over one column.
                //
                // Thread 1 Columns : 0,2,4,6,...
                // Thread 2 Columns:  1,3,5,7,...
                //
                for(int j=offset; j<b_cols; ++j, ++j)
                {
                    var sum =InnerLoop(i, j);
                    lock(c)
                    {
                        c[i, j]=sum;
                    }
                    // Debug.WriteLine("C[{0},{1}]={2}", i, j, sum);
                }
            }
        }
        re.Set();
    }

    public double InnerLoop(int a_row, int b_col)
    {
        double sum=0;
        for(int i=0; i<a_cols; i++)
        {
            sum+=a[a_row, i]*b[i, b_col];
        }
        return sum;
    }
}

class Program
{
    static void Main(string[] args)
    {
        const int N=5;
        double[,] a = new double[N,N], b=new double[N,N], c=new double[N,N];
        MatrixCalc calc=new MatrixCalc(a, b, c);
        // Fill in some values into arrays
        for(int i=0; i<N; i++)
        {
            a[i, i]=1;
            b[i, i]=1;
            if(i>0)
            {
                b[i, 0]=-i;
                a[0, i]=i;
            }
        }

        calc.Multiply();

        // Debug.WriteLine("Result: {0}", calc.OK);
    }
}