我刚刚阅读了一篇关于线程的精彩教程,并且遇到了锁问题。我需要一些提示/建议,指出我正确的方向。我想了解为什么输出没有像我期望的那样排序。代码显示了我的简单示例。
class Program {
class A {
public object obj = new object();
public int i;
}
class B {
public object obj = new object();
public int j;
}
static void Main() {
Console.Write("Thread1: ");
A a = new A();
for (a.i = 0; a.i < 9; a.i++) {
lock (a) {
new Thread(() => { Console.Write(a.i); }).Start();
}
}
Thread.Sleep(500);
Console.Write("\nThread2: ");
B b = new B();
for (b.j = 0; b.j < 9; b.j++) {
new Thread(() => { lock (b) { Console.Write(b.j); } }).Start();
}
Console.ReadLine();
}
}
Example output:
Thread1: 222456799
Thread2: 233357889
Link to the tutorial:
http://www.albahari.com/threading/
答案 0 :(得分:2)
您只是在创建线程时锁定,或者(在第二种情况下)访问该值。锁必须由所有线程使用,否则它们什么也不做。这是试图获得阻止的锁的行为。即使您确实锁定了两个线程,也无法帮助您在特定时间点将每个线程与a.i
(等)的值结合(不再存在)。
同样,线程按照自己的节奏工作;除非你有一个工人和队列,否则你不能保证订单;或者你实施自己的重新订购。
它将以自己的速度运行,并且由于您正在捕获变量 a
,因此字段a.i
完全可能在线程发生变化得到Console.Write
。相反,您应该通过复制来捕获值:
A a = new A();
for (a.i = 0; a.i < 9; a.i++) {
var tmp = a.i;
new Thread(() => { Console.Write(tmp); }).Start();
}
(或者可能完全删除a
)
for (int i = 0; i < 9; i++) {
var tmp = i;
new Thread(() => { Console.Write(tmp); }).Start();
}
答案 1 :(得分:1)
这里有几个问题:
首先,当你创建一个线程时,你就锁定了一个线程,所以创建了线程,但是你的原始主线程然后释放锁并继续在循环中进行卡车运输,同时创建线程同时运行。
您希望将第一个锁移动到使用A到Thread委托的线程中,如下所示:
for(a.i=0;a.i<9;a.i++)
{
int id=a.i;
new Thread(()=>{ lock(a){Console.Out.WriteLine("Thread{0} sees{1}",id,a.i)};}).Start(); // lots of smileys here :)
}
如果你仔细观察,你会注意到A和B没有以相同的方式锁定线程,这告诉你线程过着自己的生活和线程创建!=线程生命。
即使锁定了您的线程运行程序,您也可以在线程1在线程2之后运行的情况下结束...但由于您的锁定它们将永远不会同时运行。
您还在所有线程中引用共享成员:a.i
。该成员在主线程中初始化,不会锁定任何内容,因此您的行为是不可预测的。这就是为什么我添加捕获的变量i
,它在创建线程时抓取a.i的值,并以安全的方式在线程委托中使用。
此外,始终锁定非公共实例。如果您锁定A,请确保没有人看到A并有机会锁定它。
答案 2 :(得分:1)
因为锁始终由主线程保持,因为您在获取锁定后启动线程并且一旦获得就没有争用。现在线程可以自由运行,但是主线程启动的线程不会被任何锁同步。接近你的预期的东西是跟随(仅订单)再次计数取决于你有多快和多少核心。观察b.j ++现在在锁内。
for (b.j = 0; b.j < 9; )
{
new Thread(() => { lock (b) { Console.Write(b.j); b.j++; } }).Start();
}
锁定或关键部分背后的基本思想是只允许一件事发生,而不是顺序,在上面的修改中我已经锁定了增量操作,那就是在下一个线程开始运行代码锁定之前,当前线程有在释放锁之前完成在其获取的锁下运行所有代码。