线程安全地将线程函数的返回结果分配给变量

时间:2015-07-14 08:07:44

标签: c# multithreading

我有一个函数,它将变量作为参数并返回计算结果。该函数分为其他函数,每个函数都进行自己的计算。我需要该函数来运行多线程。

我的代码:

for (int i = 0; i < pic.Width; i++)
{
    for (int k = 0; k < pic.Height; k++)
    {
        var localK = k;
        var localI = i;
        Image bestPic;
        new Thread(() =>
        {
           bestPic = new Bitmap(getBestPic(argb));//THIS IS WHERE THE WRONG VALUES ARE ASSIGNED BECAUSE OF CROSS THREADING
           lock (thisLock)
           {
              g.DrawImage(bestPic, localI * bestPic.Width, localK * bestPic.Height, bestPic.Width, bestPic.Height);
           }
        }).Start();
    }
}

我需要的只是函数getBestPic来运行多线程。但是如何运行getBestPic函数多线程并将返回的结果赋值给bestPic变量原子?

如果需要,我的整个节目:这是一个蒙太奇计划。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Drawing;

namespace test
{
public partial class Form1 : Form
{
    private static readonly Object thisLock = new Object();

    private Graphics g;
    private Bitmap returnImg;
    private Bitmap pic;
    private int done = 0;
    private int pictureWidthAndLength = 200;
    private string inputPicName = "test";

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        DateTime dtStart = DateTime.Now;

        pic = new Bitmap(inputPicName + ".jpg");
        //MessageBox.Show(pic.GetPixel(1,1).ToArgb().ToString());
        //MessageBox.Show(pic.Width.ToString() + " x " + pic.Height.ToString());
        returnImg = new Bitmap(pic.Width * pictureWidthAndLength, pic.Height * pictureWidthAndLength);

        using (g = Graphics.FromImage(returnImg))
        {
            Color clr;
            int[] argb = new int[4];

            for (int i = 0; i < pic.Width; i++)
            {
                for (int k = 0; k < pic.Height; k++)
                {
                    clr = pic.GetPixel(i, k);
                    argb[0] = clr.A;
                    argb[1] = clr.R;
                    argb[2] = clr.G;
                    argb[3] = clr.B;

                    var localK = k;
                    var localI = i;
                    Image bestPic;
                    if (cbxthreading.Checked)
                    {
                        new Thread(() =>
                        {
                            bestPic = new Bitmap(getBestPic(argb));
                            lock (thisLock)
                            {
                                g.DrawImage(bestPic, localI * bestPic.Width, localK * bestPic.Height, bestPic.Width, bestPic.Height);
                                done++;
                            }
                        }).Start();
                    }
                    else
                    {
                        //Single threaded
                        bestPic = new Bitmap(getBestPic(argb));
                        g.DrawImage(bestPic, localI * pictureWidthAndLength, localK * pictureWidthAndLength, pictureWidthAndLength, pictureWidthAndLength);
                    }

                    //MessageBox.Show(getBestPic(argb));     
                }
            }

            if (cbxthreading.Checked)
            {
                int loopNum = pic.Width * pic.Height;
                while (done < loopNum) { }
            }
        }


        DateTime dtEnd = DateTime.Now;

        MessageBox.Show((dtEnd - dtStart).ToString());

    }

    //Get picture that is best suited to replace pixel
    private string getBestPic(int[] argb)
    {
        int numOfpics = 5;
        int[] currentBest = new int[2];
        currentBest[0] = 255;
        currentBest[1] = 150;

        for (int i = 0; i < numOfpics; i++)
        {
            int compare = compareARGB(getAverageRGB(new Bitmap((i + 1).ToString()+".jpg")), argb);
            if (compare < currentBest[0])
            {
                currentBest[0] = compare;
                currentBest[1] = i + 1;
            }
        }
        return currentBest[1].ToString() + ".jpg";
    }

    // smaller the value, closer the camparison
    private int compareARGB(int[] one, int[] two)
    {
        int [] tmp = new int[4];
        tmp[0] = Convert.ToInt32(Math.Abs(one[0] - two[0]));
        tmp[1] = Convert.ToInt32(Math.Abs(one[1] - two[1]));
        tmp[2] = Convert.ToInt32(Math.Abs(one[2] - two[2]));
        tmp[3] = Convert.ToInt32(Math.Abs(one[3] - two[3]));

        return (tmp[0] + tmp[1] + tmp[2] + tmp[3]);
    }

    //return int arry with size 4 containing the argb values
    private int[] getAverageRGB(Bitmap img)
    {
        Color clr;
        int aplha = 0;
        int red = 0;
        int green = 0;
        int blue = 0;
        for (int i = 0; i < img.Width; i++)
        {
            for (int k = 0; k < img.Height; k++)
            {
                clr = img.GetPixel(i, k);
                aplha += clr.A;
                red += clr.R;
                green += clr.G;
                blue += clr.B;
            }
        }

        aplha = aplha / (img.Width * img.Height);
        red = red / (img.Width * img.Height);
        green = green / (img.Width * img.Height);
        blue = blue / (img.Width * img.Height);

        int[] re = new int[] {aplha,red,green,blue};

        return re;
    }

    private void button2_Click(object sender, EventArgs e)
    {
        returnImg.Save(inputPicName+".bmp");
        MessageBox.Show("Done!");
    }
}
}

单线程功能有效,但需要很长时间。多线程功能也在单线程的三分之一时间内完成,但结果不正确。

2 个答案:

答案 0 :(得分:1)

正如我所理解的,

getBestPic()方法运行多线程。但问题是argb参数。您初始化它们然后在for循环中覆盖它的值。argb是引用类型,因此只有引用传递给getBestPic(),因此它的引用值在{{{}}处理时会被更改1}}。 我会尝试通过Value传递它或将getBestPic()行移动到第二个for循环的内部,所以每次初始化新变量。有关传递引用类型参数here的更多信息。

答案 1 :(得分:0)

只需在Value方法中创建argb getBestPic()的副本,然后使用它而不是使用原始版本

 private string getBestPic(int[] argb)
  {
    int[] argbCopy = argb.ToArray();
    int numOfpics = 5;
    int[] currentBest = new int[2];
    currentBest[0] = 255;
    currentBest[1] = 150;

    for (int i = 0; i < numOfpics; i++)
    {
        int compare = compareARGB(getAverageRGB(new Bitmap((i + 1).ToString()+".jpg")), argbCopy);
        if (compare < currentBest[0])
        {
            currentBest[0] = compare;
            currentBest[1] = i + 1;
        }
    }
    return currentBest[1].ToString() + ".jpg";
}