我在这里有一个函数可以在多个线程中调用另一个函数。在第二个函数中,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
}
}
}
}
}
答案 0 :(得分:2)
确保使用lock
部分重新评估对重新评估的访问权限,然后您将从正确的方向开始。你必须设计你的锁定不要创建竞争条件和死锁。
编辑:因此,在审核了您的代码之后,这里有一些评论
看起来您启动了图形功能,而图形功能又在多个线程上执行graph1threader功能。我不会质疑这是否是必要的 - 假设你已经确定它是。
活动顺序
在继续第二个循环之前,您似乎不会停止等待所有graph1threader
个线程完成。所以这是一个问题:
如果是这样,你看过Task
吗?您可以将Thread
与Task
完全交换,而不是创建线程,然后,一旦完成所有Task对象的创建并启动它们,就可以将Task.WaitAll
放在for (int j = 0; j < number; j++)
之后{1}}循环等待它们完成,然后再执行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 以便稍后使用
所有这些都可能需要防范多线程race conditions等。
如何保护共享字段
无论您是否等待,都需要保护graph1threader
中的共享字段。所以,如果我已正确阅读您的代码:
q++
循环中使用for
)graph1threader
中线程之间的设置值:
refValuesd[x, k]++;
threshValuesd[x, k] = 0.8;
y_Valuesd[x,k]++;
y__Valuesd[x,k]++;
yValuesd[x,k]++;
(旁注:这些变量名称对我来说是不可理解的,您可以尝试使用比 yValuesd
,y_Valuesd
和更具描述性的方式命名它们。 em> 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
循环,但这会慢一些,但是如果它们也锁定同一个对象,你可能需要允许其他线程更频繁地继续。 / p>
注意:这种使用共享锁的技术假设您希望graph1threader
个线程与for
中的第三个graph1
循环同时运行(即我不需要关于Task
个对象的评论)。如果不是这种情况,我认为你可以在每个函数中创建一个本地对象并锁定它。因此,每个线程都有一个不同的锁对象。因为没有线程同时访问数组中的同一个插槽,所以这只会强制执行内存屏障,并确保所有线程在读取时都看到相同的值。
对不起,这太长了,很难知道从哪里开始,而不知道你为构建这段代码所做的假设。
答案 1 :(得分:2)
那里可能存在一些问题,但我可以发现两个问题:
首先,线程正在尝试refValuesd[x, k]++
,这不是线程安全的。
请改为尝试:
Interlocked.Increment(ref refValuesd[x, k]);
其次,在使用它们生成的数据之前,您不会等待所有线程终止。尝试在for (int j = 0; j < 366; j++)
行之前添加此内容:
foreach (var thread in threads)
thread.Join();
看起来你需要学习很多东西,所以我建议你阅读这本免费的电子书: