博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#异步调用与线程总结
阅读量:6478 次
发布时间:2019-06-23

本文共 5600 字,大约阅读时间需要 18 分钟。

委托调用、子线程程调用、与线程池调用

1,委托调用
(1),同步委托:委托的Invoke方法用来进行同步调用。同步调用也可以叫阻塞调用,它将阻塞当前线程,然后执行调用,调用完毕后再继续向下进行。
 从下面的例子中可以看到,同步委托的执行是在主线程main中执行的,所以当执行委托时,当前工作会处于等待状态,开始执行委托,当委托执行完后在继续执行“当前工作”

public
delegate
int
AddHandler(
int
i,
int
y);
private
void
button1_Click(
object
sender, EventArgs e)
{
//
添加当前主线程名称“main”
Thread.CurrentThread.Name
=
"
main
"
;
AddHandler handler
=
new
AddHandler(Add);
Debug.WriteLine(handler.Invoke(
1
,
2
));
Debug.WriteLine(
"
OK
"
);
}
int
Add(
int
x,
int
y)
{
//
输出当前执行操作的现场
Debug.WriteLine(Thread.CurrentThread.Name);
return
x
+
y;
}

输出结果:main

               OK
 从Debug.WriteLine(Thread.CurrentThread.Name) 看出同步委托代码执行所在的线程与调用方式相关,同步委托代码执行所在的线程等于调用委托所在的线程.
(2),异步委托:异步调用不阻塞主线程,而是把调用在线程池中的新线程中执行,我们可以不必关心,也无需关心这个“新线程”是怎么定义的
     委托的异步调用通过BeginInvoke和EndInvoke来实现。
 从下面的例子中可以看到,异步委托的执行是在新线程中执行,所以当执行委托时,当前工作会不会阻塞,异步委托 与当前线程是同时执行的。        

public
delegate
int
AddHandler(
int
i,
int
y);
private
void
button1_Click(
object
sender, EventArgs e)
{
//
添加当前主线程名称“main”
Thread.CurrentThread.Name
=
"
main
"
;
AddHandler handler
=
new
AddHandler(Add);
Debug.WriteLine(handler.BeginInvoke(
1
,
2
,
null
,
null
));
Debug.WriteLine(
"
OK
"
);
}
int
Add(
int
x,
int
y)
{
//
输出当前执行操作的现场
Debug.WriteLine(Thread.CurrentThread.Name);
return
x
+
y;
}

输出结果:OK

               空  
 Debug.WriteLine(Thread.CurrentThread.Name) 输出为空,看出异步委托代码执行是在我们没有指定名字的新线程中执行的。
 备注:由于异步委托时启用线程池线程执行,.Net没有赋予程序员直接停止其调用的方法,使得我们没有办法直接控制委托的停止和执行,假设 Add是一个0-100循环,一般情况下我们是没有办法 在委托循环到50让委托停下来的,二般情况是可以通过一些特殊的手段的需要的话就Goolge一下吧!所以显得委托调用不够灵活

下面给一个一步委托返回值的列子:

public
delegate
int
AddHandler(
int
i,
int
y);
private
void
button1_Click(
object
sender, EventArgs e)
{
//
添加当前主线程名称“main”
Thread.CurrentThread.Name
=
"
main
"
;
AddHandler handler
=
new
AddHandler(Add);
IAsyncResult obj
=
handler.BeginInvoke(
1
,
2
,
null
,
null
);
//
使用EndInvoke方法接收返回值
int
i
=
handler.EndInvoke(obj);
Debug.WriteLine(i.ToString());
}
int
Add(
int
x,
int
y)
{
//
输出当前执行操作的现场
Debug.WriteLine(Thread.CurrentThread.Name);
return
x
+
y;
}

2,子线程调用:子线程的最大特点是在子线程执行任务时候,不占用主线程,而且我们可以自由控制它。Visual C#中使用的线程都是通过自命名空间System.Threading中的Thread类经常实例化完成的。通过Thread类的构造函数来创建可供Visual C#使用的线程,通过Thread中的方法和属性来设定线程属性和控制线程的状态。以下Thread类中的最典型的构造函数语法,在Visual C#中一般使用这个构造函数来创建、初始化Thread实例。        

private
void
button1_Click(
object
sender, EventArgs e)
{
//
添加当前主线程名称“main”
Thread.CurrentThread.Name
=
"
main
"
;
//
通过Thread类的构造函数线程,并指示一个委托让线程 执行指定方法
Thread t
=
new
Thread(
new
ThreadStart(Add));
t.Name
=
"
子线程
"
;
//
开始新线程
t.Start();
Debug.WriteLine(Thread.CurrentThread.Name);
}
void
Add()
{
for
(
int
i
=
0
; i
<
100000
; i
++
)
{
//
输出当前执行操作的线程名
Debug.WriteLine(Thread.CurrentThread.Name
+
i);
}
}

        输出结果:

        main
        子线程0
        子线程2
        子线程3
        从输出结果我们 可以看到 ,新线程的执行,不会阻塞主线程。 我们可以通过 Abort()方法结束线程。这里就不给出代码了。
        下面说一下,带参数的线程委托,看下面的代码:  

void
Add(
int
q)
{
for
(
int
i
=
0
; i
<
q; i
++
)
{
//
输出当前执行操作的线程名
Debug.WriteLine(Thread.CurrentThread.Name
+
i);
}
}
private
void
button1_Click(
object
sender, EventArgs e)
{
Thread.CurrentThread.Name
=
"
main
"
;
Thread t
=
new
Thread(
new
ThreadStart(Add(
100
)));
t.Name
=
"
子线程
"
;
t.Start();
Debug.WriteLine(Thread.CurrentThread.Name);
}

    如果你像这样 Thread t = new Thread(Add(100));传入参数的话肯定是不可能的 因为委托时不能带参数的,这里提供一种简单的解决方法,就是在线程委托中再委托的办法实现

private
void
button1_Click(
object
sender, EventArgs e)
{
//
添加当前主线程名称“main”
Thread.CurrentThread.Name
=
"
main
"
;
//
在线程委托中再定义一个委托在新委托中调用方法void Add(int q)。
Thread t
=
new
Thread(
new
ThreadStart(
delegate
{Add(
1000
); }));
t.Name
=
"
子线程
"
;
//
开始新线程
t.Start();
Debug.WriteLine(Thread.CurrentThread.Name);
}

带返回值的:

private
void
button1_Click(
object
sender, EventArgs e)
{
//
添加当前主线程名称“main”
Thread.CurrentThread.Name
=
"
main
"
;
//
定义一个变量准备接收子线程返回值
int
iResult
=
0
;
//
在线程委托中再定义一个委托在新委托中调用方法void Add(int q)。
Thread t
=
new
Thread(
new
ThreadStart(
delegate
{iResult
=
Add(
1000
); }));
t.Name
=
"
子线程
"
;
//
开始新线程
t.Start();
//
设置一个循环来等待子线程结束
while
(t.ThreadState
!=
System.Threading.ThreadState.Stopped)
{
t.Join(
10
);
}
Debug.WriteLine(iResult.ToString());
Debug.WriteLine(Thread.CurrentThread.Name);
}

3,线程池调用 :“线程池”是可以用来在后台执行多个任务的线程集合。这使主线程可以自由地异步执行其他任务。

线程池通常用于服务器应用程序。每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会占用主线程,也不会延迟后续请求的处理。
ThreadPool(线程池)是一个静态类,它没有定义任何的构造方法(),我们只能够使用它的静态方法,这是因为,这是因为ThreadPool是托管线程池,是由CLR管理的。
ThreadPool使用WaitCallback委托,它所要做的工作是在后台进行的。使工作项的排队和运行更容易,可以给工作者线程传递一个状态对象(提供数据)。状态对象是私有的作用域位于线程层,所以不需要进行同步。
ThreadPool目标是为了减除线程的初始化开销,实现并行处理。
一个ThreadPool里面注册的线程拥有默认的堆栈大小,默认的优先级。并且,他们都存在于多线程空间(Multithreaded apartment)中。

ThreadPool中的Thread不能手动取消,也不用手动开始。所以ThreadPool并不适用比较长的线程。你要做的只是把一个WaitCallback委托塞给ThreadPool,然后剩下的工作将由系统自动完成。系统会在ThreadPool的线程队列中一一启动线程。

当线程池满时,多余的线程会在队列里排队,当线程池空闲时,系统自动掉入排队的线程,以保持系统利用率。

我们的程序中使用ThreadPool来进行一些比较耗时或者需要阻塞的操作。当学要复杂的同步技术,例如事件,或需要对一个现场表调用Join方法时线程池就不能满足需求了.在以下情况中不宜使用ThreadPool而应该使用单独的Thread 

private
void
button1_Click(
object
sender, EventArgs e)
{
//
添加当前主线程名称“main”
Thread.CurrentThread.Name
=
"
main
"
;
//
使用线程池ThreadPool创建线程
ThreadPool.QueueUserWorkItem(Add);
Debug.WriteLine(Thread.CurrentThread.Name);
}
//
这里加obj参数是为了适应委托格式 public delegate void WaitCallback(object state)
//
包含回调方法要使用的信息的对象。
void
Add(
object
obj)
{
for
(
int
i
=
0
; i
<
100000
; i
++
)
{
//
输出当前执行操作的现场
Debug.WriteLine(Thread.CurrentThread.Name
+
i);
}
}

可以同样用委托再委托的方法 调用带有参数(或没有任何参数)的方法

private
void
button1_Click(
object
sender, EventArgs e)
{
//
添加当前主线程名称“main”
Thread.CurrentThread.Name
=
"
main
"
;
//
使用线程池ThreadPool创建线程
ThreadPool.QueueUserWorkItem(
delegate
{ Add(
333
,
44
); },
"
111
"
);
Debug.WriteLine(Thread.CurrentThread.Name);
}
void
Add(
object
obj,
int
q)
{
for
(
int
i
=
0
; i
<
q; i
++
)
{
//
输出当前执行操作的现场
Debug.WriteLine(Thread.CurrentThread.Name
+
i);
}
}

转载于:https://www.cnblogs.com/stulife/archive/2012/01/12/2320221.html

你可能感兴趣的文章
css3 Tab Menus Without Javascript
查看>>
android P设置状态栏字体图标颜色
查看>>
【ZZ】HTTP status codes
查看>>
Druid数据库密码加密
查看>>
Win7设置FTP详细过程
查看>>
Android ViewPager使用详解
查看>>
webservice调用报异常,xfire,nullpointer,serialize……
查看>>
向tomcat学习jmx
查看>>
win7删除SVN脚本
查看>>
我的友情链接
查看>>
Android 一个改善的okHttp封装库
查看>>
centos6.x 抓取ssh登录的用户名和密码
查看>>
我从程序猿到DBA的进化之路
查看>>
ClassLoader原理扩展
查看>>
MongoDB基本使用
查看>>
CAT 测试事件开关
查看>>
php实现工厂模式
查看>>
mirror driver学习(二. 安装)
查看>>
zz:控制台程序后台运行
查看>>
【超清视频】CCNA系列课程之三:交换机工作原理及VLAN创建
查看>>