我的程序每次一次运行n
个任务组中的任务。
每个任务将数据写入自己的Queue<string>
对象,由队列Queue<string>
中List<Queue<string>>
的索引提供。
任务不共享数据或队列,但仍然出现同步错误。
我知道数据结构不是线程安全的,我也不知道为什么应该这样做,为什么会出错,因为每个Task
都有自己的数据结构,什么原因会导致错误?
下面是一个简单的代码来演示:
class Program
{
static int j = 0;
List<Queue<string>> queueList = new List<Queue<string>>();
public void StartTasts(int n)
{
for (int i = 0; i < n; i++)
queueList.Add(new Queue<string>());
List<Task> tsk = new List<Task>();
for (int TaskGroup = 0; TaskGroup < 10; TaskGroup++)
{ //10 groups of task
//each group has 'n' tasks working in parallel
for (int i = 0; i < n; i++)
{
//each task gets its own and independent queue from the list
tsk.Add(Task.Factory.StartNew(() =>
{
DoWork(j % n);
}));
j++;
}
//waiting for each task group to finish
foreach (Task t in tsk)
t.Wait();
//after they all finished working with the queues, clear queues
//making them ready for the nest task group
foreach (Queue<string> q in queueList)
q.Clear();
}
}
public void DoWork(int queue)
{
//demonstration of generating strings
//and put them in the correct queue
for (int k = 0; k < 10000; k++)
queueList[queue].Enqueue(k + "");
}
static void Main(string[] args)
{
new Program().StartTasts(10);
}
}
该程序会产生一些错误,例如:
System.ArgumentException:'目标数组不够长。检查destIndex和length,以及数组的下限。'
System.IndexOutOfRangeException:'索引超出数组的范围。' (在队列中)
System.AggregateException:发生一个或多个错误。 ---> System.ArgumentException:源数组不够长。检查srcIndex和length,以及数组的下界。
和更多错误,不会出现在序列号案件中。 我很想知道为什么,因为我看不到这些任务如何使彼此的独立队列混乱。
答案 0 :(得分:4)
问题是正常的变量关闭问题。因为所有任务共享变量j的相同实例,所以它们都将共享相同的值,所以很可能发生的是您的循环超快地启动了10个任务,但是在它们中的任何一个都不能达到j % n
的值之前j已经变成10。
制作在for循环范围内声明的k的本地副本,它应该可以解决您的问题。
public void StartTasts(int n)
{
for (int i = 0; i < n; i++)
queueList.Add(new Queue<string>());
List<Task> tsk = new List<Task>();
for (int TaskGroup = 0; TaskGroup < 10; TaskGroup++)
{ //10 groups of task
//each group has 'n' tasks working in parallel
for (int i = 0; i < n; i++)
{
int k = j; // `int k = i;` would work here too and give you the same results.
tsk.Add(Task.Factory.StartNew(() =>
{
DoWork(k % n);
}));
j++;
}
//waiting for each task group to finish
foreach (Task t in tsk)
t.Wait();
//after they all finished working with the queues, clear queues
//making them ready for the nest task group
foreach (Queue<string> q in queueList)
q.Clear();
}
}
如果您想通过简单的操作来查看问题所在,请尝试使用此简单的代码。
public static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
int j = i;
Task.TaskFactory.StartNew(() =>
{
Thread.Sleep(10); //Give a little time for the for loop to complete.
Console.WriteLine("i: " + i + " j: " + j);
}
});
Console.ReadLine();
}
答案 1 :(得分:1)
您已在任务内部计算出taskId,并在任务外部更改了计算基础。 我仅略微更改了逻辑。我没有任何错误。
namespace Project1
{
using System.Collections.Generic;
using System.Threading.Tasks;
internal class Program
{
private static int j = 0;
private readonly List<Queue<string>> queueList = new List<Queue<string>>();
public void StartTasts(int n)
{
for (var i = 0; i < n; i++)
{
this.queueList.Add(new Queue<string>());
}
var taskList = new List<Task>();
for (var taskGroup = 0; taskGroup < 10; taskGroup++)
{
// 10 groups of task
// each group has 'n' tasks working in parallel
for (var i = 0; i < n; i++)
{
// each task gets its own and independent queue from the list
var taskId = j % n;
taskList.Add(
Task.Factory.StartNew(
() =>
{
this.DoWork(taskId);
}));
j++;
}
// waiting for each task group to finish
foreach (var t in taskList)
{
t.Wait();
}
// after they all finished working with the queues, clear queues
// making them ready for the nest task group
foreach (var q in this.queueList)
{
q.Clear();
}
}
}
public void DoWork(int queue)
{
// demonstration of generating strings
// and put them in the correct queue
for (var k = 0; k < 10000; k++)
{
this.queueList[queue].Enqueue(k + string.Empty);
}
}
private static void Main(string[] args)
{
new Program().StartTasts(10);
}
}
}
答案 2 :(得分:0)