一个关于多线程同步的问题

时间:2022-11-08 18:09:42
using System;
using System.Threading;
using System.Diagnostics;

class App
{
public int i=0, j=0;
public int Limit = 1000;
public void Increase()
{
while(i < Limit)
{
try
{
Monitor.Enter(this);
i++;
Thread.Sleep(1);
j++;
}
finally
{
Monitor.Exit(this);
}
}
}

public void Check()
{
while(i < Limit)
{
try
{
Monitor.Enter(this);
if(i != j)
{
Debug.WriteLine("i != j, i="+i+" j="+j);
Thread t = Thread.CurrentThread;
t.Abort();
}
}
finally
{
Monitor.Exit(this);
}
Thread.Sleep(1);
}
}
  
public static void Main() 
{
App app = new App();

Thread t1 = new Thread(new ThreadStart(app.Increase));
Thread t2 = new Thread(new ThreadStart(app.Increase));
Thread t3 = new Thread(new ThreadStart(app.Check));
t1.Start();
t2.Start();
t3.Start();
}
}

有如上代码,请问Monitor.Enter(this)是监视App类的一个实例app还是只监视app的某个函数?例如app.Increase。据某本书上写着,Monitor.Enter(this)是监视app.Increase的,为了达到t3检测到i和j的值相等,第一步先要做的是在Increase函数里加上一个Monitor,以达到t1增加i和j的值时t2不能也来和t1同时对函数app.Increase操作,所以此时i和j的值总是相等,这个可以理解,但是书上又说这样是不够的,还会检测到i和j的值不等,其中一个原因就是当i加了1时,还没等到j加上1,app.Check就对i,j进行检测,而解决这个问题的办法是在Check里也加一个Monitor,最终就形成了上面的完整代码了。

我所不明白的是:在我理解,Check上没加Monitor和有加Monitor是没区别的,得到的都是i和j不相等,因为即使在Check加Monitor,它同样可以在Increase的i刚加1后检测i和j的值,Increase的Monitor只是保证此时不能有别的线程调用Increase函数,但不保证此时不能有别的线程访问i和j的值啊。

可是按照上面完整代码的运行结果,检测出的i和j永远是相等的,为什么呢?

10 个解决方案

#1


Monitor.Enter(this)应该是监视app对象的吧

#2


假如是app对象的话,问题又来啦,当t1开始运行后,如果是锁定app对象不能被其他线程读写的话,按道理Check不管是否有加Monitor,在t1运行时t3都不能访问app对象啊,当t1运行完毕后,t3 要访问app时,i和j的值必定相等。但为什么Check没加Monitor时检测到的i和j不等,但有加Monitor时检测到相等呢?

#3


呵呵,Int32最大是2,147,483,647

public int Limit = 1000;

-〉

public int Limit = 2000000000;

哥们的机器要是够BT,Monitor和不Monitor还一样,那这样:

//public int i=0, j=0;
//public int Limit = 1000;
  public Int64 i=0, j=0;
  public Int64 Limit = 9000000000000;

#4


此处是研究学术问题,不是讨论机器的性能问题,到目前为止本人还未得出确凿的结论为什么会出现以上的现象,对于以上一段代码又该如何去理解,请赐教。

#5


我希望得到的是一种不考虑机器性能的单纯的结论。

#6


using System;
using System.Threading;
using System.Diagnostics;

class App2
{
public int i=0, j=0;
public int Limit = 1000;
public void Increase()
{
while(i < Limit)
{
try
{
Console.WriteLine("Increase: Before Monitor.Enter(this)" + "  ---  " +Thread.CurrentThread.Name+"\n");
Monitor.Enter(this);
Console.WriteLine("Increase: After Monitor.Enter(this) Before i++"+"  ---  " + Thread.CurrentThread.Name+"\n");
i++;
Thread.Sleep(1);
j++;
Console.WriteLine(Thread.CurrentThread.Name+"  Control The Object<App2> Successfully!"+" i="+i.ToString()+" j="+j.ToString()+"\n");
}
finally
{
Monitor.Exit(this);
}
}

}

public void Check()
{

try
{
Console.WriteLine("Check: Before Monitor.Enter(this)"+"---" +Thread.CurrentThread.Name);
Monitor.Enter(this);
Console.WriteLine("Check:After Monitor.Enter(this)"+"---" +Thread.CurrentThread.Name);
if(i != j)
{
Debug.WriteLine("i != j, i="+i+" j="+j);
Thread t = Thread.CurrentThread;
t.Abort();
}
}
finally
{
// Monitor.Exit(this);
}
Thread.Sleep(1);

}
  
static Thread t1,t2,t3;
public static void Main() 
{
App2 app = new App2();

t1 = new Thread(new ThreadStart(app.Increase));
t2 = new Thread(new ThreadStart(app.Increase));
t3 = new Thread(new ThreadStart(app.Check));
t1.Name = "t1";
t2.Name = "t2";
t3.Name = "t3";
t1.Start();
t2.Start();
t3.Start();
Console.ReadLine();
}
}

楼主看看能不能帮你找找感觉

#7


顶到最前面去

#8


up

#9


不会,问题太难啦

#10


我的理解是monitor锁定对象是为了告诉临界区的代码是属于哪个对象的。当前线程(获得对象锁的线程)使用这个对象执行临界区的代码。

当check没有使用monitor的时候,肯定会返回i<>j的情况。因为i和j是所有线程共享的对象,当t1线程修改了i,没有修改j的时候,这个时候t2线程没有获得对象锁阻塞,由于t3线程没有使用monitor,换句话说t3线程不必阻塞,就能直接访问i和j,显然这个时候i<>j。

当t3线程使用了monitor,则也会被阻塞。

#1


Monitor.Enter(this)应该是监视app对象的吧

#2


假如是app对象的话,问题又来啦,当t1开始运行后,如果是锁定app对象不能被其他线程读写的话,按道理Check不管是否有加Monitor,在t1运行时t3都不能访问app对象啊,当t1运行完毕后,t3 要访问app时,i和j的值必定相等。但为什么Check没加Monitor时检测到的i和j不等,但有加Monitor时检测到相等呢?

#3


呵呵,Int32最大是2,147,483,647

public int Limit = 1000;

-〉

public int Limit = 2000000000;

哥们的机器要是够BT,Monitor和不Monitor还一样,那这样:

//public int i=0, j=0;
//public int Limit = 1000;
  public Int64 i=0, j=0;
  public Int64 Limit = 9000000000000;

#4


此处是研究学术问题,不是讨论机器的性能问题,到目前为止本人还未得出确凿的结论为什么会出现以上的现象,对于以上一段代码又该如何去理解,请赐教。

#5


我希望得到的是一种不考虑机器性能的单纯的结论。

#6


using System;
using System.Threading;
using System.Diagnostics;

class App2
{
public int i=0, j=0;
public int Limit = 1000;
public void Increase()
{
while(i < Limit)
{
try
{
Console.WriteLine("Increase: Before Monitor.Enter(this)" + "  ---  " +Thread.CurrentThread.Name+"\n");
Monitor.Enter(this);
Console.WriteLine("Increase: After Monitor.Enter(this) Before i++"+"  ---  " + Thread.CurrentThread.Name+"\n");
i++;
Thread.Sleep(1);
j++;
Console.WriteLine(Thread.CurrentThread.Name+"  Control The Object<App2> Successfully!"+" i="+i.ToString()+" j="+j.ToString()+"\n");
}
finally
{
Monitor.Exit(this);
}
}

}

public void Check()
{

try
{
Console.WriteLine("Check: Before Monitor.Enter(this)"+"---" +Thread.CurrentThread.Name);
Monitor.Enter(this);
Console.WriteLine("Check:After Monitor.Enter(this)"+"---" +Thread.CurrentThread.Name);
if(i != j)
{
Debug.WriteLine("i != j, i="+i+" j="+j);
Thread t = Thread.CurrentThread;
t.Abort();
}
}
finally
{
// Monitor.Exit(this);
}
Thread.Sleep(1);

}
  
static Thread t1,t2,t3;
public static void Main() 
{
App2 app = new App2();

t1 = new Thread(new ThreadStart(app.Increase));
t2 = new Thread(new ThreadStart(app.Increase));
t3 = new Thread(new ThreadStart(app.Check));
t1.Name = "t1";
t2.Name = "t2";
t3.Name = "t3";
t1.Start();
t2.Start();
t3.Start();
Console.ReadLine();
}
}

楼主看看能不能帮你找找感觉

#7


顶到最前面去

#8


up

#9


不会,问题太难啦

#10


我的理解是monitor锁定对象是为了告诉临界区的代码是属于哪个对象的。当前线程(获得对象锁的线程)使用这个对象执行临界区的代码。

当check没有使用monitor的时候,肯定会返回i<>j的情况。因为i和j是所有线程共享的对象,当t1线程修改了i,没有修改j的时候,这个时候t2线程没有获得对象锁阻塞,由于t3线程没有使用monitor,换句话说t3线程不必阻塞,就能直接访问i和j,显然这个时候i<>j。

当t3线程使用了monitor,则也会被阻塞。