C# - 任务结果问题
C# - Problems with task results
我必须通过字符串比较在 758.000 个地址的列表中找到重复的地址。
到目前为止我做了什么:
public int StartDuplicateFinder(List<Address> addresses, List<Address> checklist)
{
int found = 0;
foreach(Address addr1 in addresses)
{
List<Address> addresses2 = checklist.FindAll(
delegate (Address addr2)
{
return addr2.AddressString == addr1.AddressString && addr2.Duplicate == ""
&& addr2.AddrIndex > addr1.AddrIndex;
}
);
foreach(Address addr2 in addresses2)
{
addr2.Duplicate = "1";
found++;
}
}
return found;
}
这需要大约 7 个小时(太长了)并交付大约 93.000 个副本!
为了加快速度,我将 checklist
分成 4 个部分(200k、200k、200k 和 158k)的 List<Address> checklists
,并像这样使用 Task
:
public class Worker
{
private List<Address> addresses = null;
private List<Addresses> checklist = null
private int found = 0;
public Worker(List<Address> _addresses, List<Address> _checklist)
{
addresses = _addresses; //always 758.000 addresses
//with 4 Tasks: Task 1, 2 and 3 = 200.000 addresses, Task 4 = 158.000 addresses
//with 6 Tasks: Task 1, 2, 3, 4 and 5 with 150.000 addresses, Task 6 with 8.000 addresses
checklist = _checklist;
}
public int StartDuplicateFinder()
{
foreach(Address addr1 in addresses)
{
List<Address> addresses2 = checklist.FindAll(
delegate (Address addr2)
{
return addr2.AddressString == addr1.AddressString && addr2.Duplicate == ""
&& addr2.AddrIndex > addr1.AddrIndex;
}
);
foreach(Address addr2 in addresses2)
{
addr2.Duplicate = "1";
found++;
}
}
}
public int Found {get {return found;}}
}
private async void StartTasks(List<Task> Tasklist)
{
foreach (Task t in Tasklist)
{
t.Start();
}
await Task.WhenAll(Tasklist.ToArray());
}
private void DoSomething()
{
List<Task> Tasklist = new List<Task>();
foreach(List<Address> checklist in checklists)
{
Worker w = new Worker(addresses, checklist);
Tasklist.Add(new Task(StartDuplicateFinder));
}
StartTasks(Tasklist);
//wait until the tasks are finished
//do other stuff
...
}
现在只有 运行s 35 分钟!但是,如果我查看重复项,发现存在巨大偏差。仅找到大约 27.000 个重复项。
我试了几次,每次都得到不同的结果。
4 Tasks, first run: 4 Tasks, second run:
Task# > duplicates Task# > duplicates
1 > 749 1 > 689
2 > 2450 2 > 2391
3 > 10304 3 > 10073
4 > 14462 4 > 14282
Sum > 27965 Sum > 27435
6 Tasks, first run: 6 Tasks, second run:
Task# > duplicates Task# > duplicates
1 > 16 1 > 24
2 > 56 2 > 55
3 > 202 3 > 236
4 > 679 4 > 634
5 > 852 5 > 800
6 > 2985 6 > 2981
Sum > 4790 Sum > 4730
每次都是 758.000 个地址的相同列表。
我用 Task
、Thread
和 BackgroundWorker
试过了,但总是得到不同的结果!
如果我 运行 在 1 个任务中执行此操作,结果总是 92.377 次重复(我认为这是正确的)。
谁能帮我解决这个问题?
问题中的代码不足,无法推测。如果您真的想排除故障,您将需要一个完整的、独立的存储库。但这种行为的典型原因是您在多个线程中修改共享数据。根据经验,并行工作者应该对只读数据进行操作,返回它们的结果以由主线程累积。
但不是并行处理,而是考虑使用更高效的数据结构和算法。 IE 而不是嵌套循环连接,构建哈希 [=17=](Dictionary<TKey,TValue>
或 Lookup<TKey,TValue>
)以使其在单个线程上更快(至少作为第一步)。例如:
public int StartDuplicateFinder(List<Address> addresses, List<Address> checklist)
{
int found = 0;
var checklistByAddressString = checklist.ToLookup(a => a.AddressString, a => a);
foreach (Address addr1 in addresses)
{
var addressMatches = checklistByAddressString[addr1.AddressString];
var addresses2 = addressMatches.Where(addr2 => addr2.Duplicate == ""
&& addr2.AddrIndex > addr1.AddrIndex);
foreach (Address addr2 in addresses2)
{
addr2.Duplicate = "1";
found++;
}
}
return found;
}
问题是您在循环浏览 addresses
列表中的项目时正在修改它们。你正在创造竞争条件。
作为标准的一部分,您的过滤器具有:
addr2.Duplicate == ""
稍后您更改项目:
addr2.Duplicate = "1"`.
也许你最好分块 addresses
并反对完整 checkList
或者,仅在针对两个列表的单线程上使用 LINQ query
。与手动循环集合相比,您很可能会获得更快的结果。
我必须通过字符串比较在 758.000 个地址的列表中找到重复的地址。 到目前为止我做了什么:
public int StartDuplicateFinder(List<Address> addresses, List<Address> checklist)
{
int found = 0;
foreach(Address addr1 in addresses)
{
List<Address> addresses2 = checklist.FindAll(
delegate (Address addr2)
{
return addr2.AddressString == addr1.AddressString && addr2.Duplicate == ""
&& addr2.AddrIndex > addr1.AddrIndex;
}
);
foreach(Address addr2 in addresses2)
{
addr2.Duplicate = "1";
found++;
}
}
return found;
}
这需要大约 7 个小时(太长了)并交付大约 93.000 个副本!
为了加快速度,我将 checklist
分成 4 个部分(200k、200k、200k 和 158k)的 List<Address> checklists
,并像这样使用 Task
:
public class Worker
{
private List<Address> addresses = null;
private List<Addresses> checklist = null
private int found = 0;
public Worker(List<Address> _addresses, List<Address> _checklist)
{
addresses = _addresses; //always 758.000 addresses
//with 4 Tasks: Task 1, 2 and 3 = 200.000 addresses, Task 4 = 158.000 addresses
//with 6 Tasks: Task 1, 2, 3, 4 and 5 with 150.000 addresses, Task 6 with 8.000 addresses
checklist = _checklist;
}
public int StartDuplicateFinder()
{
foreach(Address addr1 in addresses)
{
List<Address> addresses2 = checklist.FindAll(
delegate (Address addr2)
{
return addr2.AddressString == addr1.AddressString && addr2.Duplicate == ""
&& addr2.AddrIndex > addr1.AddrIndex;
}
);
foreach(Address addr2 in addresses2)
{
addr2.Duplicate = "1";
found++;
}
}
}
public int Found {get {return found;}}
}
private async void StartTasks(List<Task> Tasklist)
{
foreach (Task t in Tasklist)
{
t.Start();
}
await Task.WhenAll(Tasklist.ToArray());
}
private void DoSomething()
{
List<Task> Tasklist = new List<Task>();
foreach(List<Address> checklist in checklists)
{
Worker w = new Worker(addresses, checklist);
Tasklist.Add(new Task(StartDuplicateFinder));
}
StartTasks(Tasklist);
//wait until the tasks are finished
//do other stuff
...
}
现在只有 运行s 35 分钟!但是,如果我查看重复项,发现存在巨大偏差。仅找到大约 27.000 个重复项。
我试了几次,每次都得到不同的结果。
4 Tasks, first run: 4 Tasks, second run:
Task# > duplicates Task# > duplicates
1 > 749 1 > 689
2 > 2450 2 > 2391
3 > 10304 3 > 10073
4 > 14462 4 > 14282
Sum > 27965 Sum > 27435
6 Tasks, first run: 6 Tasks, second run:
Task# > duplicates Task# > duplicates
1 > 16 1 > 24
2 > 56 2 > 55
3 > 202 3 > 236
4 > 679 4 > 634
5 > 852 5 > 800
6 > 2985 6 > 2981
Sum > 4790 Sum > 4730
每次都是 758.000 个地址的相同列表。
我用 Task
、Thread
和 BackgroundWorker
试过了,但总是得到不同的结果!
如果我 运行 在 1 个任务中执行此操作,结果总是 92.377 次重复(我认为这是正确的)。
谁能帮我解决这个问题?
问题中的代码不足,无法推测。如果您真的想排除故障,您将需要一个完整的、独立的存储库。但这种行为的典型原因是您在多个线程中修改共享数据。根据经验,并行工作者应该对只读数据进行操作,返回它们的结果以由主线程累积。
但不是并行处理,而是考虑使用更高效的数据结构和算法。 IE 而不是嵌套循环连接,构建哈希 [=17=](Dictionary<TKey,TValue>
或 Lookup<TKey,TValue>
)以使其在单个线程上更快(至少作为第一步)。例如:
public int StartDuplicateFinder(List<Address> addresses, List<Address> checklist)
{
int found = 0;
var checklistByAddressString = checklist.ToLookup(a => a.AddressString, a => a);
foreach (Address addr1 in addresses)
{
var addressMatches = checklistByAddressString[addr1.AddressString];
var addresses2 = addressMatches.Where(addr2 => addr2.Duplicate == ""
&& addr2.AddrIndex > addr1.AddrIndex);
foreach (Address addr2 in addresses2)
{
addr2.Duplicate = "1";
found++;
}
}
return found;
}
问题是您在循环浏览 addresses
列表中的项目时正在修改它们。你正在创造竞争条件。
作为标准的一部分,您的过滤器具有:
addr2.Duplicate == ""
稍后您更改项目:
addr2.Duplicate = "1"`.
也许你最好分块 addresses
并反对完整 checkList
或者,仅在针对两个列表的单线程上使用 LINQ query
。与手动循环集合相比,您很可能会获得更快的结果。