1

我这里有一个函数,它在多个线程中调用另一个函数。在第二个函数中,refValuesd[,] 的每个元素的值为 1。但是当我在调用多个线程后在 graph1() 函数中检查同一二维数组的元素时,我得到的元素的值不同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

                    }

                }

            }


        }

    }
4

2 回答 2

2

确保您使用部分围绕对 refvalued 的访问lock,您将从正确的方向开始。你必须设计你的锁定,而不是创建竞争条件和死锁。

编辑:因此,在查看了您的代码后,这里有一些评论

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

事件顺序

在继续第二个循环之前,您似乎不会停下来等待所有graph1threader线程完成。所以这里有一个问题:

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

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

        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(在 graph1q++的第二个for循环中使用)
  • 因此,以下行将不会在 中的线程之间竞争设置值graph1threader
    • refValuesd[x, k]++;
    • threshValuesd[x, k] = 0.8;
    • y_Valuesd[x,k]++;
    • y__Valuesd[x,k]++;
    • yValuesd[x,k]++;

旁注:这些变量名称对我来说是不可理解的,您可以尝试以比 更具描述性的方式命名它们 yValuesdy_Valuesd y__Valuesd 帮助您稍后调试)。

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

private readonly object SyncRoot = new object();

然后围绕对我上面提到的任何共享字段的所有访问书签 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同时运行(即不需要我对对象的评论)。如果不是这种情况,我认为您可以在每个函数中创建一个本地对象并锁定它。因此,每个线程都会有不同的锁对象。因为没有线程同时访问数组中的同一插槽,所以这只会强制执行内存屏障并确保所有线程在读取它们时看到相同的值。forgraph1Task

抱歉,这太长了,如果不了解您构建此代码所经历的假设,很难知道从哪里开始。

于 2013-05-13T13:26:23.847 回答
2

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

首先,线程试图做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/

于 2013-05-13T13:35:37.977 回答