无论我使用什么:基于螺纹类或TPL任务模式。数据总是有限制的索引。从进一步的研究中,我发现计数器的价值可以是4,甚至不可能。我错过了什么?我希望您的专家意见!
通过Visual Studio测试15.8(2017(16.1(2019(,项目定位.NET框架4.72。
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
// a multi-threading search demo, omit much code for simple and clear
// generate 0-99, total 100 elements with ascending order
List<int> testData = new List<int>();
for (int i = 0; i < 100; i++)
{
testData.Add(i);
}
List<int> searchFor = new List<int>() {
67, 0, 99,
23, 24, 25,
-1, 106
};
const int threadsCount = 4;
// Test switch
bool useThreadInsteadOfTaskTPL = true;
if (useThreadInsteadOfTaskTPL)
{
// search every piece of data
for (int j = 0; j < searchFor.Count; j++)
{
Thread[] threads = new Thread[threadsCount];
Console.WriteLine("Search for: {0}", searchFor[j]);
// trying to divide the data into 4 parts, and search in parallel
for (int i = 0; i < threadsCount; i++)
{
Thread thread = new Thread(() => {
// Capture the counters to make sure no lambda pitfall
int counterI = i;
int counterJ = j;
Console.WriteLine("i value: {0}", counterI);
Console.WriteLine("j value: {0}", counterJ);
// your code
});
threads[i] = thread;
threads[i].Start();
}
for (int i = 0; i < threads.Length; i++)
{
threads[i].Join();
}
Console.WriteLine();
}
}
else
{
for (int j = 0; j < searchFor.Count; j++)
{
Task[] tasks = new Task[threadsCount];
Console.WriteLine("Search for: {0}", searchFor[j]);
// trying to divide the data into 4 parts, and search in parallel
for (int i = 0; i < threadsCount; i++)
{
Task task = Task.Factory.StartNew(() => {
// Capture the counters to make sure no lambda pitfall
int counterI = i;
int counterJ = j;
Console.WriteLine("i value: {0}", counterI);
Console.WriteLine("j value: {0}", counterJ);
// your code
}, new CancellationTokenSource().Token,
TaskCreationOptions.None, TaskScheduler.Default);
tasks[i] = task;
}
Task.WaitAll(tasks);
Console.WriteLine();
}
}
Console.ReadKey();
}
}
}
我的期望值应该通过0 ... 3,但是i的实际值可能等于4或在迭代之间保持不变。
您应该在循环开始时重新选择i
和j
(不在lambda内(:
for (int i = 0; i < threadsCount; i++)
{
// Capture the counters to make sure no lambda pitfall
int counterI = i;
int counterJ = j;
Thread thread = new Thread(() =>
{
Console.WriteLine("i value: {0}", counterI);
Console.WriteLine("j value: {0}", counterJ);
// your code
}
}
您的线程被安排进行执行(在调用Start()
之后未立即启动它(,并且当它开始运行i
(和j
(的值时,它已经可以更改。(您可以查看此情况的编译器生成的代码,以及您的(。
且任务相同 - 它们是安排的,没有立即开始。
更多详细信息:
请参见此示例(使用Action
代表代替Thread
(和生成代码。
您可以看到差异(生成的代码创建了类的实例 哪个存储要打印的价值和实际打印的方法(:
- 在委托内部内部 - 对于每次迭代,都使用相同的实例,并在调用委托后的值会增加。使用
Action
,它可以按预期工作,因为它立即执行(从生成类调用方法要打印值(,然后生成类的值会增加并新开始迭代。 - reassign在代表外部 - 创建了生成类的实例对于每一次迭代,因此没有增量。每个迭代都有独立实例和下一个迭代无法更改上一个。
在线程的情况下,唯一的区别是没有立即启动线程,它计划执行,这需要一些时间。对于第一种情况 - 当调用打印值的方法时,该值可以增加(由于所有迭代的实例(,并且您会得到意外结果。
您可以通过多次运行应用程序(对于第一种情况(进行检查 - 打印i
变量时,您将不会获得相同的结果 - 有时它在不预期时会增加(因为它花了一些时间来调用Start()
和实际启动。安排后的线程执行(,有时值正确(因为线程是安排的,并且在增加 Start()
后几乎立即启动(。