多线程不能正确执行
本文关键字:执行 不能 多线程 | 更新日期: 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
吗?而不是创建线程,您几乎可以将Thread
与Task
交换,然后,一旦您完成了所有任务对象的创建并启动它们,您可以将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
对象的评论是不需要的)。如果不是这种情况,我认为您可以在每个函数中创建一个局部对象并锁定它。因此,每个线程都有一个不同的锁对象。因为没有线程在同一时间访问数组中的同一个槽,这只会强制内存屏障,并确保所有线程在读取它们时看到相同的值。
对不起,这篇文章太长了,如果不了解更多构建这段代码的假设,很难知道从哪里开始。