C# 关于Monitor

Anod 2012-07-11 09:12:53

public partial class Form1 : Form
{
private ThreadStart myStart;
private ParameterizedThreadStart myPStart;
int m, n, t;
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
myStart = new ThreadStart(ShowNamea);
Thread thA = new Thread(myStart);
myPStart = new ParameterizedThreadStart(ShowNameObj);
Thread thB = new Thread(myPStart);
thA.Start();
thB.Start(2);

}
private void ShowNamea()
{
while(true)
{
Thread.Sleep(100);
try
{
Monitor.Enter(t);
if (t % 2 != 0)
{
Monitor.Wait(t);
}
t += 2;
Monitor.Pulse(t);
}
finally
{
Monitor.Exit(t);
} m++;
}
}
private void ShowNameObj(object obj)
{
int num = (int)obj;
while (true)
{
Thread.Sleep(100);
try
{
Monitor.Enter(t);
if (t % 2 != num)
{
Monitor.Wait(t);
}
t += num;
Monitor.Pulse(t);
}
finally
{
Monitor.Exit(t);
}
n++;
}
}
}


运行会报“从不同步的代码块中调用了对象同步方法”的异常,求解惑。
...全文
410 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
ViewStates 2012-07-11
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]

引用 1 楼 的回复:
只能Monitor引用类型,不能是值类型

C# code

public partial class Form1 : Form
{
private ThreadStart myStart;
private ParameterizedThreadStart myPStart;
int m, n, s =……
[/Quote]
你装箱拆箱后T已经不是之前的T了。。然后会导致在发送信号时已经不是LOCK时的线程了
Only the current owner of the lock can signal a waiting object using Pulse.

The thread that currently owns the lock on the specified object invokes this method to signal the next thread in line for the lock. Upon receiving the pulse, the waiting thread is moved to the ready queue. When the thread that invoked Pulse releases the lock, the next thread in the ready queue (which is not necessarily the thread that was pulsed) acquires the lock.
Anod 2012-07-11
  • 打赏
  • 举报
回复
如果不用Monitor锁定的对象传数据就没问题,用了就出错。代码修改容易,要么对Monitor专门创建一个引用类型来管理锁定代码段的访问,要么直接用lock(虽然Lock实际上还是Monitor),我需要的是理解为什么,MSDN上没看到对此有详细解释。求大神解释。
Anod 2012-07-11
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 的回复:]
只能Monitor引用类型,不能是值类型
[/Quote]

public partial class Form1 : Form
{
private ThreadStart myStart;
private ParameterizedThreadStart myPStart;
int m, n, s = 0;
object t;
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
t = s;
myStart = new ThreadStart(ShowNamea);
Thread thA = new Thread(myStart);
myPStart = new ParameterizedThreadStart(ShowNameObj);
Thread thB = new Thread(myPStart);
thA.Start();
thB.Start(2);

}
private void ShowNamea()
{
while(true)
{
Thread.Sleep(100);
try
{
Monitor.Enter(t);
if ((int)t % 2 != 0)
{
Monitor.Wait(t);
}
t =(int)t+ 2;
Monitor.Pulse(t);
}
finally
{
Monitor.Exit(t);
} m++;
}
}
private void ShowNameObj(object obj)
{
int num = (int)obj;
while (true)
{
Thread.Sleep(100);
try
{
Monitor.Enter(t);
if ((int)t % 2 != num)
{
Monitor.Wait(t);
}
t =(int)t + num;
Monitor.Pulse(t);
}
finally
{
Monitor.Exit(t);
}
n++;
}
}
}

刚才已经注意到了,但依旧不行啊。。。
SocketUpEx 2012-07-11
  • 打赏
  • 举报
回复
只能Monitor引用类型,不能是值类型
Anod 2012-07-11
  • 打赏
  • 举报
回复
老问题不是使用Monitor.Pulse的原因,我是在学习C#的多线程控制,不单是使用,而是尽量的深入理解。刚才C++转过来。

你看贴不认真哇。我上面在5L时已经找到了老问题的原因所在,并发现新问题求解,但是你在10L还没发现。。。。。

这时间我已经把新问题搞定了,原因是我对IL还没摸索明白,try块的范围指定在方法的最后放着的,之前翻的快没看到。
      .try IL_001d to IL_0049
finally handler IL_0049 to IL_0051
.try IL_000e to IL_0055
finally handler IL_0055 to IL_0058

通过这个还可以发现 Lock块本身是带有一个try块的,即在异常时保证它内部使用的Monitor.Exit可以执行。好了 这个帖子可以结贴了
lizhibin11 2012-07-11
  • 打赏
  • 举报
回复
虽然你改成了object,但你在获得锁之后,对t重新赋值,指向了另一个对象,退出时指向的就又是不同的对象了。
另外我不知道你为什么用Monitor.Pulse,它的用法看一下那篇文章。
Anod 2012-07-11
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 的回复:]
补充上面少复制了一段:lock的代码段退出是这样的

C# code
IL_0046: nop
IL_0047: leave.s IL_0051
IL_0049: ldloc.1
IL_004a: call void [mscorlib]System.Threading.Monitor::……
[/Quote]

晕了 还是没复制完全,竟然没看清楚是两个leave:
      IL_003c:  box        [mscorlib]System.Int32
IL_0041: stfld object WindowsApplication1.Form1::t
IL_0046: nop
IL_0047: leave.s IL_0051
IL_0049: ldloc.1
IL_004a: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_004f: nop
IL_0050: endfinally
IL_0051: nop
IL_0052: nop
IL_0053: leave.s IL_0058
IL_0055: nop
IL_0056: nop
IL_0057: endfinally
IL_0058: nop

还没明白为什么
Anod 2012-07-11
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 lizhibin11 的回复:]
int装箱导致,不同代码段锁的对象是不一样的,在同一个代码段中,进入和释放的对象也是不一样的。另外我看到还有Monitor.Pulse,它的用法最好看一下这篇经典文章http://topic.csdn.net/u/20111209/18/2fa51d2e-ba01-4c28-a7d2-2719e6de3c8f.html
[/Quote]

哈哈 问题已经找到了,新问题能否解决?
lizhibin11 2012-07-11
  • 打赏
  • 举报
回复
int装箱导致,不同代码段锁的对象是不一样的,在同一个代码段中,进入和释放的对象也是不一样的。另外我看到还有Monitor.Pulse,它的用法最好看一下这篇经典文章http://topic.csdn.net/u/20111209/18/2fa51d2e-ba01-4c28-a7d2-2719e6de3c8f.html
Anod 2012-07-11
  • 打赏
  • 举报
回复
补充上面少复制了一段:lock的代码段退出是这样的
      IL_0046:  nop        
IL_0047: leave.s IL_0051
IL_0049: ldloc.1
IL_004a: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_004f: nop
IL_0050: endfinally
IL_0051: nop

注意到它是Monitor.Exit的参数是ldloc.1,也就是之前使用lock的方法内声明的object变量,这个保证了即使lock代码段内对t进行了各种操作,也不会造成如Monitor那样的异常。
Anod 2012-07-11
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 的回复:]
引用 2 楼 的回复:

引用 1 楼 的回复:
只能Monitor引用类型,不能是值类型

C# code

public partial class Form1 : Form
{
private ThreadStart myStart;
private ParameterizedThreadStart myPStart;
int m, n, s =……

你装箱拆箱后……
[/Quote]

你的意思是装箱后,Lock和Monitor.Enter的t不是同一个?这个很难去理解。装箱的IL代码是一样的,

IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.0
IL_0003: ldfld int32 WindowsApplication1.Form1::s
IL_0008: box [mscorlib]System.Int32
IL_000d: stfld object WindowsApplication1.Form1::t
,根据MSDN对装箱的定义,s被装箱后会被创建一个新的实例存储在t中,所以我觉得这里t是没有变的。
再看Lock开始时的代码:
     IL_0005:  nop        
IL_0006: ldc.i4.s 100
IL_0008: call void [mscorlib]System.Threading.Thread::Sleep(int32)
IL_000d: nop
IL_000e: nop
IL_000f: ldarg.0
IL_0010: ldfld object WindowsApplication1.Form1::t
IL_0015: dup
IL_0016: stloc.1

IL_0017: call void [mscorlib]System.Threading.Monitor::Enter(object)
IL_001c: nop

Monitor.Enter的IL代码少了红色部分,红色部分的意思是执行时会复制t并存储在使用lock的方法起始时声明的一个object变量中。而Monitor是没有的。


呃,在准备复制下面代码的时候我明白了,就复制上来吧,免得以后又忘了:

IL_0045: stloc.0
IL_0046: ldarg.0
IL_0047: ldloc.0
IL_0048: box [mscorlib]System.Int32
IL_004d: stfld object WindowsApplication1.Form1::t

Monitor的线程在使用t中的数据后会进行装箱,这时stfld指令会将装箱后生成的不同引用传给t,从而导致Enter和Pulse、Exit的不一致,使得线程出现异常。

呵呵,原问题解决了,新问题出来了,


lock的代码段退出很简单,直接Leave掉
      IL_0052:  nop        
IL_0053: leave.s IL_0058
IL_0055: nop
IL_0056: nop
IL_0057: endfinally

而Monitor的:
 IL_0048:  box        [mscorlib]System.Int32
IL_004d: stfld object WindowsApplication1.Form1::t
IL_0052: ldarg.0
IL_0053: ldfld object WindowsApplication1.Form1::t
IL_0058: call void [mscorlib]System.Threading.Monitor::Pulse(object)
IL_005d: nop
IL_005e: nop
IL_005f: leave.s IL_0070
IL_0061: nop
IL_0062: ldarg.0
IL_0063: ldfld object WindowsApplication1.Form1::t
IL_0068: call void [mscorlib]System.Threading.Monitor::Exit(object)
IL_006d: nop
IL_006e: nop
IL_006f: endfinally

IL_0070: nop
IL_0071: ldarg.0
IL_0072: dup
IL_0073: ldfld int32 WindowsApplication1.Form1::m
IL_0078: ldc.i4.1
IL_0079: add
IL_007a: stfld int32 WindowsApplication1.Form1::m

为什么Leave指令把endfinally 也给跳过了,这不相当于是出现了异常但没有执行finally块吗?我的程序有错,但也不应该这里有错吧,真心不明白!!!
内容概要:本文围绕不确定环境下的多式联运路径优化问题展开研究,提出并实现了基于AFO算法、遗传算法(GA)和粒子群优化算法(PSO)的三种智能优化方法,并借助Matlab平台完成算法编程与仿真。研究构建了考虑时间、成本、转运风险等多重不确定因素的路径优化模型,系统比较了AFO、GA、PSO三种算法在收敛速度、全局寻优能力和稳定性方面的表现,同时引入Matlab自带的全局优化搜索器作为基准对照,深入分析各算法在复杂物流网络中的适用边界与性能差异。研究表明,AFO算法在解决此类组合优化问题时展现出更快的收敛效率和更强的局部规避能力。; 适合人群:具备一定Matlab编程基础与运筹优化知识,从事物流工程、交通运输规划、智能算法开发等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于多式联运、综合货运网络中的路径决策支持系统构建;②为不确定性条件下复杂路径规划问题提供智能算法选型依据与技术实现方案;③支持科研人员复现主流优化算法并开展横向性能对比实验,推动算法改进与实际落地。; 阅读建议:建议读者结合提供的Matlab代码逐模块分析算法实现流程,重点理解目标函数设计、约束条件处理及参数敏感性分析部分,可通过调整问题规模与算法参数进行对比实验,进一步拓展至动态路径规划或大规模网络优化等延伸场景。

111,129

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧