【问题标题】:Thread safe assigning of the returned result of a threaded function to a variable线程安全地将线程函数的返回结果分配给变量
【发布时间】:2015-07-14 18:22:10
【问题描述】:

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

我的代码:

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

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

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      只需在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";
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-19
        • 1970-01-01
        • 2019-07-03
        • 2022-01-06
        • 1970-01-01
        • 2010-10-02
        相关资源
        最近更新 更多