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

本文关键字:线程 结果 分配 变量 返回 安全 函数 | 更新日期: 2023-09-27 18:02:29

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

我的代码:

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!");
    }
}
}

单线程功能可以工作,但需要很长时间。多线程功能的完成时间也是单线程功能的三分之一,但结果并不正确。

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

正如我所理解的,

getBestPic()方法运行多线程。但问题出在argb参数上。您将其初始化为个,然后在for循环中覆盖其值。argb是引用类型,所以只有引用被传递给getBestPic(),所以它的引用值在getBestPic()中处理时会发生更改。我会尝试通过Value传递它,或者将int[] argb = new int[4];行移动到第二个for循环的内部,这样每次都可以初始化新的变量。有关在此处传递引用类型参数的详细信息。

只需在getBestPic()方法中创建argbValue的副本并使用它,而不是使用原始的

 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";
}