我正在尝试理解多线程的基础知识,所以我构建了一个小程序,提出了一些问题,我将非常感谢您的帮助:)
这是一个小程序:
class Program
{
public static int count;
public static int max;
static void Main(string[] args)
{
int t = 0;
DateTime Result;
Console.WriteLine("Enter Max Number : ");
max = int.Parse(Console.ReadLine());
Console.WriteLine("Enter Thread Number : ");
t = int.Parse(Console.ReadLine());
count = 0;
Result = DateTime.Now;
List<Thread> MyThreads = new List<Thread>();
for (int i = 1; i < 31; i++)
{
Thread Temp = new Thread(print);
Temp.Name = i.ToString();
MyThreads.Add(Temp);
}
foreach (Thread th in MyThreads)
th.Start();
while (count < max)
{
}
Console.WriteLine("Finish , Took : " + (DateTime.Now - Result).ToString() + " With : " + t + " Threads.");
Console.ReadLine();
}
public static void print()
{
while (count < max)
{
Console.WriteLine(Thread.CurrentThread.Name + " - " + count.ToString());
count++;
}
}
}我通过一些测试运行检查了这一点:
我设置了最大值100,看起来最快的执行时间是使用2个线程,比使用10个线程快80%。
问题:
1)线程4-10一次都不打印,怎么会打印呢?
2)线程越多不是应该更快吗?
我设置了最大值10000,并禁用了打印。
在这种配置下,5个线程似乎是最快的。
与第一次检查相比,为什么会有变化?
而且在这种配置中(使用打印),所有线程都打印几次。为什么这与第一次运行时只打印了几个线程不同?
有没有办法让所有的线程一个接一个地打印?排成一排还是类似的?
非常感谢您的帮助:)
发布于 2011-07-06 05:47:09
您的代码无疑是进入线程世界的第一步,并且您刚刚经历了(许多)令人头疼的第一步!
首先,static可以让您在线程之间共享变量,但它不能以线程安全的方式做到这一点。这意味着不能保证您的count < max表达式和count++是最新的,也不能保证它们是线程之间的有效保护。查看max仅为10时的程序输出(在我的8处理器工作站上,t设置为4 ):
T0 - 0
T0 - 1
T0 - 2
T0 - 3
T1 - 0 // wait T1 got count = 0 too!
T2 - 1 // and T2 got count = 1 too!
T2 - 6
T2 - 7
T2 - 8
T2 - 9
T0 - 4
T3 - 1 // and T3 got count = 1 too!
T1 - 5对于每个线程逐个打印的问题,我假设您正在尝试协调对count的访问。您可以使用同步原语(如C#中的lock语句)来实现这一点。下面是对您的代码的一个简单修改,它将确保只出现max增量:
static object countLock = new object();
public static void printWithLock()
{
// loop forever
while(true)
{
// protect access to count using a static object
// now only 1 thread can use 'count' at a time
lock (countLock)
{
if (count >= max) return;
Console.WriteLine(Thread.CurrentThread.Name + " - " + count.ToString());
count++;
}
}
}这个简单的修改使你的程序在逻辑上是正确的,但也很慢。该示例现在显示了一个新问题:锁争用。现在每个线程都在争夺对countLock的访问权。我们已经让我们的程序线程安全了,但是没有任何并行的好处!
线程和并行性并非易事,但值得庆幸的是,.Net的最新版本都附带了Task Parallel Library (TPL)和Parallel LINQ (PLINQ)。
这个库的美妙之处在于它可以非常容易地转换当前代码:
var sw = new Stopwatch();
sw.Start();
Enumerable.Range(0, max)
.AsParallel()
.ForAll(number =>
Console.WriteLine("T{0}: {1}",
Thread.CurrentThread.ManagedThreadId,
number));
Console.WriteLine("{0} ms elapsed", sw.ElapsedMilliseconds);
// Sample output from max = 10
//
// T9: 3
// T9: 4
// T9: 5
// T9: 6
// T9: 7
// T9: 8
// T9: 9
// T8: 1
// T7: 2
// T1: 0
// 30 ms elapsed上面的输出是一个有趣的例子,说明了为什么线程会给新用户带来“意想不到的结果”。当线程并行执行时,它们可能在不同的时间点完成代码块,或者一个线程可能比另一个线程更快。你永远不会真正了解线程!
发布于 2011-07-06 05:44:58
你的打印函数远不是线程安全的,这就是为什么4-10不打印的原因。所有线程共享相同的max和count变量。
更多线程减慢速度的原因很可能是每次处理器在每个线程之间改变焦点时发生的状态变化。
此外,当您创建大量线程时,系统需要分配新的线程。大多数情况下,建议使用任务,因为它们是从系统管理的线程池中拉出的。因此不一定要分配。创建一个截然不同的新线程是相当昂贵的。
不管怎么说,看看这里:http://msdn.microsoft.com/en-us/library/aa645740(VS.71).aspx
发布于 2011-07-06 05:56:50
看起来很小心:
t = int.Parse(Console.ReadLine());
count = 0;
Result = DateTime.Now;
List<Thread> MyThreads = new List<Thread>();
for (int i = 1; i < 31; i++)
{
Thread Temp = new Thread(print);
Temp.Name = i.ToString();
MyThreads.Add(Temp);
}我想你遗漏了一个变量t(i< 31)。
在编写代码之前,您应该阅读许多关于并行和多线程编程的书籍,因为编程语言只是一种工具。祝好运!
https://stackoverflow.com/questions/6589128
复制相似问题