多线程不能正确执行

本文关键字:执行 不能 多线程 | 更新日期: 2023-09-27 18:13:14

这里有一个函数,它在多个线程中调用另一个函数。在第二个函数中,refValuesd[,]的每个元素的值都是1。但是,当我在调用多个线程后检查graph1()函数中相同2D数组的元素时,我得到了refValuesd[,]元素的不同值。我是一个多线程新手。

void graph1()
    {
        for (int j = 0; j < 366; j++) //loop to refresh element values
        {
            refValues[j] = 0;
            threshValues[j] = 0;
            y_Values[j] = 0;
            y__Values[j] = 0;
            yValues[j] = 0;
            for (int k = 0; k < 1000; k++)
            {
                threshValuesd[k,j] = 0;
                refValuesd[k,j] = 0;
                y__Valuesd[ k,j] = 0;
                y_Valuesd[k,j] = 0;
                yValuesd[k,j] = 0;
            }
        }
        List<string>[] list = new List<string>[4];//manpower details
        list = A.mandetselect();
        int number = A.Countmandet();//retuns an integer value
        string[] trade = list[1].ToArray();
        string[] license = list[2].ToArray();
        string[] training = list[3].ToArray();
        string[] display_status = list[4].ToArray();
        List<string>[] listc = new List<string>[14];//Project details
        listc = A.Select();
        int numberc = A.Count();
        string abc = "";
        int q = 0;
        for (int j = 0; j < number; j++)
        {
            if (!display_status[j].Equals("NO") && (selection == "ALL" || (selection == "ALL-LAE" && license[j] != "") || (selection == "ALL-NON LAE" && license[j] == "") || (selection == "AVIONICS -ALL" && trade[j] == "Avionics") || (selection == "AVIONICS-NON LAE" && trade[j] == "Avionics" && license[j] == "") || (selection == "AVIONICS-LAE" && trade[j] == "Avionics" && license[j] != "") || (selection == "AIRFRAME-ALL" && trade[j] == "Airframes") || (selection == "AIRFRAME-NON LAE" && trade[j] == "Airframes" && license[j] == "") || (selection == "AIRFRAME-LAE" && trade[j] == "Airframes" && license[j] != "")))
            {
                int z = numberc;
                string[] nameofproj = listc[0].ToArray();
                int copy = q;
                int copy2 = j;
                string a = abc;
                string[] name = list[0].ToArray();
                List<string>[] lista = new List<string>[5];
                string[] status = listc[13].ToArray();
                thread[copy] = new Thread(() => graph1threader(a, name[copy2], lista, z, nameofproj, status, copy));
                thread[copy].Start();
                q++;
            }
        }

        for (int j = 0; j < 366; j++)
        {
            for (int k = 0; k < q; k++)
            {
                threshValues[j] += threshValuesd[k, j];                   
                refValues[j] += refValuesd[k, j];
                y__Values[j] += y__Valuesd[k, j];
                y_Values[j] += y_Valuesd[k, j];
                yValues[j] += yValuesd[k, j];
            }
        }

            for (int j = 0; j < 366; j++)
            {
                DateTime temp = G.AddDays(j);
                string temper = temp.ToShortDateString();
                y__Values[j] = y__Values[j] - y_Values[j];
                xNames[j] = temper;
            }
        chart1.Series[1].Points.DataBindXY(xNames, y_Values);           

        chart1.Series[2].Points.DataBindXY(xNames, y__Values);

        chart1.Series[3].Points.DataBindXY(xNames, refValues);

        chart1.Series[4].Points.DataBindXY(xNames, threshValues);

        chart1.Series[5].Points.DataBindXY(xNames, yValues);

    }

下面是在多个线程中执行的函数:

void graph1threader(string abc,string nameofj,List<string>[] lista,int numberc,string[] nameofproj,string[] status,int copy )
    {
        DBConnect A = new DBConnect();
        int x = copy;
        string[] projname;
        string[] country;
        string[] start;
        string[] end;
        abc = nameofj.Replace(" ", "_");
        lista = A.manprojselect(abc);
        projname = lista[0].ToArray();
        country = lista[2].ToArray();
        start = lista[3].ToArray();
        end = lista[4].ToArray();
        for (int k = 0; k < 366; k++)//basic
        {
            refValuesd[x, k]++;
             refValuesd[copy,k].ToString());
            threshValuesd[x, k] = 0.8;
            string Status = "";
            int flag = 0;
            for (int l = 0; l < A.Countproj(abc); l++)
            {
                for (int m = 0; m < numberc; m++)
                {
                    if (nameofproj[m] == projname[l])
                    {
                        Status = status[m];
                    }
                }

                DateTime shuru = DateTime.ParseExact(start[l],
                               "dd-MM-yyyy hh:mm:ss",
                               CultureInfo.InvariantCulture);
                DateTime anth = DateTime.ParseExact(end[l],
                               "dd-MM-yyyy hh:mm:ss",
                               CultureInfo.InvariantCulture);
                if (temp >= shuru && temp <= anth)
                {
                    if (Status != "PLANNED" && Status != "LO" && flag == 0)
                    {
                        y_Valuesd[x,k]++;//BASIC UTILISATION
                        flag = 1;
                    }
                    if (Status == "IP" || Status == "OTD")
                        y__Valuesd[x,k]++;//EXCESS
                    if (Status == "PLANNED")
                    {
                        yValuesd[x,k]++;//UNUTILISED
                    }
                }
            }

        }
    }

多线程不能正确执行

可能有一些问题,但我可以发现两个:

首先,线程正在尝试执行refValuesd[x, k]++,这不是线程安全的。

试试这个:

Interlocked.Increment(ref refValuesd[x, k]);

其次,在使用它们生成的数据之前,您不需要等待所有线程终止。试着在for (int j = 0; j < 366; j++)行之前添加这个:

foreach (var thread in threads)
    thread.Join();

看起来你还有很多要学的,所以我建议你读这本免费的电子书:

http://www.albahari.com/threading/

确保您将lock部分包围访问重估值,您将在正确的方向开始。你必须设计你的锁,以避免产生竞争条件和死锁。

EDIT:所以,在检查了你的代码之后,这里有一些注释

看起来您启动了graph函数,该函数反过来在多个线程上执行graph1threader函数。我不会质疑这是否必要——假设是你已经决定了这是必要的。

事件顺序

看起来在继续第二个循环之前,您没有停下来等待所有graph1threader线程完成。这里有一个问题:

  • 您是否希望在继续之前完成graph1threader作业?

如果是,你看过Task吗?而不是创建线程,您几乎可以将ThreadTask交换,然后,一旦您完成了所有任务对象的创建并启动它们,您可以将Task.WaitAll放在for (int j = 0; j < number; j++)循环之后等待它们完成,然后在graph1中执行第三个for循环:

        thread[copy] = new Task(() => graph1threader(a, name[copy2], lista, z, nameofproj, status, copy));
        thread[copy].Start();
        q++;
    }
}
Task.WaitAll(thread);
for (int j = 0; j < 366; j++)
{
    for (int k = 0; k < q; k++)
    {
        threshValues[j] += threshValuesd[k, j];
        refValues[j] += refValuesd[k, j];
        y__Values[j] += y__Valuesd[k, j];
        y_Values[j] += y_Valuesd[k, j];
        yValues[j] += yValuesd[k, j];
    }
}

如果你不想使用Task(或不能)Thread.Join也可以工作,但Task支持取消比Thread更容易,所以如果你有一个长时间运行操作的UI,它会让事情对你更容易。

共享字段

两个函数都使用以下变量(请忽略任何不正确的变量类型,重要的是名称):

double[,] threshValuesd;
int[,] refValuesd;
int[,] y__Valuesd;
int[,] y_Valuesd;
int[,] yValuesd;

我将这个列表命名为书签A以供以后使用

所有这些都可能需要防止多线程竞争条件等。

如何保护共享字段

无论您是否等待,您都需要保护graph1threader中的共享字段。如果我没看错代码的话:

  • 你传递一个唯一的,递增的值给graph1threader(在graph1的第二个for循环中使用q++)
  • 因此,以下行将不会竞争graph1threader中线程之间的设置值:
    • refValuesd[x, k]++;
    • threshValuesd[x, k] = 0.8;
    • y_Valuesd[x,k]++;
    • y__Valuesd[x,k]++;
    • yValuesd[x,k]++;

(旁注:这些变量名对我来说是难以理解的,你可以尝试用比 yValuesd, y_Valuesd y__Valuesd 更有描述性的方式来命名它们,以帮助你以后调试)

然而,即使线程不竞争更新相同数组槽中的值,您也可能会遇到内存障碍和对单个数组槽的读/写访问的问题。因此,我建议这样做,非常简单地声明一个类字段:

private readonly object SyncRoot = new object();

周围的所有访问我上面提到的任何共享字段bookmark A您需要使用(以您的第一个循环为例):

lock (this.SyncRoot) {
    for (int j = 0; j < 366; j++)
    {
        for (int k = 0; k < q; k++)
        {
            threshValues[j] += threshValuesd[k, j];
            refValues[j] += refValuesd[k, j];
            y__Values[j] += y__Valuesd[k, j];
            y_Values[j] += y_Valuesd[k, j];
            yValues[j] += yValuesd[k, j];
        }
    }
}

保持锁调用尽可能少,但尽可能靠近共享资源。我的意思是,如果你想的话,你可以在内部for循环中锁定,但这会变慢,但是你可能需要允许其他线程更频繁地进行,如果它们也锁定同一个对象。

注意:这种使用共享锁的技术假设您希望graph1threader线程与graph1中的第三个for循环同时运行(即我对Task对象的评论是不需要的)。如果不是这种情况,我认为您可以在每个函数中创建一个局部对象并锁定它。因此,每个线程都有一个不同的锁对象。因为没有线程在同一时间访问数组中的同一个槽,这只会强制内存屏障,并确保所有线程在读取它们时看到相同的值。

对不起,这篇文章太长了,如果不了解更多构建这段代码的假设,很难知道从哪里开始。