线程/异步委托的问题 (绝对诡异)

EsMo 2009-08-22 11:25:02

public int addhandler(int a, int b)
{

Console.WriteLine("A");
System.Threading.Thread.Sleep(3000);// 注释代码后无延迟
return a + b;
}

这是一个异步委托的回调函数


Random r = new Random();
for (int i = 0; i < 50; i++)
{
clstest c = new clstest();
int num1 = r.Next(1, 101);
int num2 = r.Next(1, 101);
add a1 = new add(c.addhandler);
a1.BeginInvoke(num1, num2, new AsyncCallback(c.addCallback), a1);
Console.WriteLine("已启动数量" + i);
}


如果循环启动这个异步委托 是不是启动一个 就该打印一个A(也有可能启动几个后打印一个A,具体跟cpu有关)
但实际结果是50个异步委托全部启动完 才开始打印A 如果注释掉回调中的 sleep方法 就会按照正常逻辑打印
可sleep明显是在打印后之后 也就是说注释sleep应该和打印A是没关系的
...全文
193 27 打赏 收藏 转发到动态 举报
写回复
用AI写文章
27 条回复
切换为时间正序
请发表友善的回复…
发表回复
windinwing 2009-08-24
  • 打赏
  • 举报
回复
--那问下 能不能实现加上sleep和去掉sleep一样的效果呢? (就是不先打印全部a的效果)
addhandler的执行速度问题,回调方法只是保证执行完成后调用方法,并不是顺序执行
 add[] a1 创建一个代理方法数组,在for一次调用。
  如果只是要打不先打印全部a,把sleep放在 Console.WriteLine("A");之前

 另外

public void addCallback(IAsyncResult result)
{
Console.WriteLine("A"); //放在这里,才表示执行完了一个会调方法,  你之前的代码仅仅表示在执行异步方法
}
zheng2852848 2009-08-24
  • 打赏
  • 举报
回复
MARK
EsMo 2009-08-24
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 windinwing 的回复:]
  

--但实际结果是50个异步委托全部启动完 才开始打印A 如果注释掉回调中的 sleep方法 就会按照正常逻辑打印
可sleep明显是在打印后之后 也就是说注释sleep应该和打印A是没关系的

 首先addhandler方法不是回调函数,回调应该是 a1.BeginInvoke(num1, num2, new AsyncCallback(c.addCallback), a1);
c.addCallback,这个才是回调函数.
实际上 for (int i = 0; i < 50; i++)里把50个异步执行加入到线程中,但是addhandler中的Console.WriteLine("A");并不一定是异步执行完成,addCallback被调用才表示addCallback异步执行完成。

--但实际结果是50个异步委托全部启动完 才开始打印A
 那是因为 for (int i = 0; i < 50; i++)很快完成,调用BeginInvoke不会聊阻塞当前一程调用后就返回,而什么时候执行并不可控,所以第一个A可能出现在任意位置,但是调用50个BeginInvoke后返回的速度快于3秒种,后面的49个A理论上都出现在for语句块执行完成. 注释以后,addhandler 执行完就返回,由于这个是在线程中执行,时实上可能在for还没执行完之前被陆续调用。。

addhandler 中Console.WriteLine("A");执行并不表示方法执行完成,

这个才是执行完成后掉用的方法
public void addCallback(IAsyncResult result)
        {
           
        }
一般异步阻塞模式是调用后不理这,等待回调方法发送通知
[/Quote]

那问下 能不能实现加上sleep和去掉sleep一样的效果呢? (就是不先打印全部a的效果)
netstray 2009-08-24
  • 打赏
  • 举报
回复
学习学习
beckfun 2009-08-24
  • 打赏
  • 举报
回复
LZ是怎么知道这段代码实际结果是50个异步委托全部启动完 才开始打印A
LZ怎么就不认为主线程被暂停了...过了3秒又自动运行了?
Console.WriteLine("已启动数量" + i); 这条语句在你的程序执行中被打印在第一行吗?
最好把你的运行结果截图上传上来....
红街咖啡 2009-08-24
  • 打赏
  • 举报
回复
我来学习的。楼主不会怪我把
windinwing 2009-08-24
  • 打赏
  • 举报
回复
  

--但实际结果是50个异步委托全部启动完 才开始打印A 如果注释掉回调中的 sleep方法 就会按照正常逻辑打印
可sleep明显是在打印后之后 也就是说注释sleep应该和打印A是没关系的

 首先addhandler方法不是回调函数,回调应该是 a1.BeginInvoke(num1, num2, new AsyncCallback(c.addCallback), a1);
c.addCallback,这个才是回调函数.
实际上 for (int i = 0; i < 50; i++)里把50个异步执行加入到线程中,但是addhandler中的Console.WriteLine("A");并不一定是异步执行完成,addCallback被调用才表示addCallback异步执行完成。

--但实际结果是50个异步委托全部启动完 才开始打印A
 那是因为 for (int i = 0; i < 50; i++)很快完成,调用BeginInvoke不会聊阻塞当前一程调用后就返回,而什么时候执行并不可控,所以第一个A可能出现在任意位置,但是调用50个BeginInvoke后返回的速度快于3秒种,后面的49个A理论上都出现在for语句块执行完成. 注释以后,addhandler 执行完就返回,由于这个是在线程中执行,时实上可能在for还没执行完之前被陆续调用。。

addhandler 中Console.WriteLine("A");执行并不表示方法执行完成,

这个才是执行完成后掉用的方法
public void addCallback(IAsyncResult result)
{

}
一般异步阻塞模式是调用后不理这,等待回调方法发送通知
jack15850798154 2009-08-23
  • 打赏
  • 举报
回复
学习!!
timbs 2009-08-23
  • 打赏
  • 举报
回复
帮顶
jeedispeed 2009-08-23
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 bejon 的回复:]
委托不是线程
异步也不是线程,异步同一个方法会按顺序执行

你的代码并不存在并发情况,所以不会出现你想要的结果
[/Quote]

异步不在新线程开启,还怎么异步呢?
redleafe 2009-08-23
  • 打赏
  • 举报
回复
学习,这块一直没搞清楚。
l8487 2009-08-23
  • 打赏
  • 举报
回复
Console是个全局共享的。。如果A,B中都有console打印的代码。在A中sleep了。。B中即使代码执行完了。也打印不出来的。。。。这样理解对不对。
codelabs 2009-08-23
  • 打赏
  • 举报
回复
因为你异步调用的是同一个方法,你试下将函数名字改变下(方法内容不变)。再调用,就是你要的结果了。
龙宜坡 2009-08-23
  • 打赏
  • 举报
回复
委托异步调用么,你用EndInvoke试试
阿牛138588 2009-08-23
  • 打赏
  • 举报
回复
委托不是线程
异步也不是线程,异步同一个方法会按顺序执行

你的代码并不存在并发情况,所以不会出现你想要的结果
LQknife 2009-08-22
  • 打赏
  • 举报
回复
Console.WriteLine("A");是在异步线程方法addhandler里的,而System.Threading.Thread.Sleep(3000)使得
addhandler方法暂停,虽然console.writeline方法执行完了,可addhandler方法还没有执行完,我做了个测试
            System.Threading.ThreadPool.QueueUserWorkItem(obj =>
{
Console.WriteLine(1);
System.Threading.Thread.Sleep(3000);
});

Console.WriteLine(2);

结果是:2 1 说明包含Console.WriteLine(1)的方法没执行完,就不会打印出1来(可惜我不会从clr运行的角度来分析)

至于为啥50次循环后才打印A,主线程循环50次创建50个委托不会花3000ms吧
微创社(MCC) 2009-08-22
  • 打赏
  • 举报
回复
打印A可能在任何时间,
虽然回调采用的是线程池,同样需要准备时间.
回调1(线程1准备中)
回调2(线程2准备中)
...
回调N(线程2准备中)
(线程1准备刚好完成)
输出A...
下面就没有规律了.

至于为什么只打印一个A
加一句
Console.ReadKey();
就能看清楚了.

A的位置,主要取决于线程切换的速度.
这个又主要取决于线程池的大小和当时状态.
CLR1.0 线程池中50个最多
CLR>=2.0 线程池中250个最多
tddlhl 2009-08-22
  • 打赏
  • 举报
回复
我的理解是在3秒内主程序已经运行结束了,而那些异步线程随着主线程的结束而结束,所以只打出一个A
  • 打赏
  • 举报
回复
学习
muyebo 2009-08-22
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 mythad 的回复:]
学习!
[/Quote]
1
加载更多回复(5)

110,537

社区成员

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

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

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