`
wfdoublext
  • 浏览: 129873 次
  • 性别: Icon_minigender_1
  • 来自: 青岛
社区版块
存档分类
最新评论

委托线程

    博客分类:
  • .net
阅读更多

本系列教程主要包括如下内容:

1.  BeginInvoke
EndInvoke方法

2.  Thread

3. 线程池

4. 线程同步基础

5. 死锁

6. 线程同步的7种方法

7. 如何在线程中访问GUI组件

一、线程概述

在操作系统中一个进程至少要包含一个线程,然后,在某些时候需要在同一个进程中同时执行多项任务,或是为了提供程序的性能,将要执行的任务分解成多个子任务执行。这就需要在同一个进程中开启多个线程。我们使用C#编写一个应用程序(控制台或桌面程序都可以),然后运行这个程序,并打开windows任务管理器,这时我们就会看到这个应用程序中所含有的线程数,如下图所示。



如果任务管理器没有“线程数”列,可以【查看】>【选择列】来显示“线程计数”列。从上图可以看出,几乎所有的进程都拥有两个以上的线程。从而可以看出,线程是提供应用程序性能的重要手段之一,尤其在多核CPU的机器上尤为明显。

二、用委托(Delegate)BeginInvokeEndInvoke方法操作线程

 

C#中使用线程的方法很多,使用委托的BeginInvokeEndInvoke方法就是其中之一。BeginInvoke方法可以使用线程异步地执行委托所指向的方法。然后通过EndInvoke方法获得方法的返回值(EndInvoke方法的返回值就是被调用方法的返回值),或是确定方法已经被成功调用。我们可以通过四种方法从EndInvoke方法来获得返回值。

三、直接使用EndInvoke方法来获得返回值

    当使用BeginInvoke异步调用方法时,如果方法未执行完,EndInvoke方法就会一直阻塞,直到被调用的方法执行完毕。如下面的代码所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace MyThread
{
    
class Program
    {
        
private static int newTask(int ms)
        {
            Console.WriteLine(
"任务开始");
            Thread.Sleep(ms);
            Random random 
= new Random();
            
int n = random.Next(10000);
            Console.WriteLine(
"任务完成");
            
return n;
        }

        
private delegate int NewTaskDelegate(int ms);
             
        
        
static void Main(string[] args)
        {
            NewTaskDelegate task 
= newTask;
            IAsyncResult asyncResult 
= task.BeginInvoke(2000nullnull);

            // EndInvoke方法将被阻塞2秒
            int result = task.EndInvoke(asyncResult);           
            Console.WriteLine(result);
        }
    }
}

    在运行上面的程序后,由于newTask方法通过Sleep延迟了2秒,因此,程序直到2秒后才输出最终结果(一个随机整数)。如果不调用EndInvoke方法,程序会立即退出,这是由于使用BeginInvoke创建的线程都是后台线程,这种线程一但所有的前台线程都退出后(其中主线程就是一个前台线程),不管后台线程是否执行完毕,都会结束线程,并退出程序。关于前台和后台线程的详细内容,将在后面的部分讲解。

    读者可以使用上面的程序做以下实验。首先在Main方法的开始部分加入如下代码:

Thread.Sleep(10000);

    以使Main方法延迟10秒钟再执行下面的代码,然后按Ctrl+F5运行程序,并打开企业管理器,观察当前程序的线程数,假设线程数是4,在10秒后,线程数会增至5,这是因为调用BeginInvoke方法时会建立一个线程来异步执行newTask方法,因此,线程会增加一个。

四、使用IAsyncResult asyncResult属性来判断异步调用是否完成

    
虽然上面的方法可以很好地实现异步调用,但是当调用EndInvoke方法获得调用结果时,整个程序就象死了一样,这样做用户的感觉并不会太好,因此,我们可以使用asyncResult来判断异步调用是否完成,并显示一些提示信息。这样做可以增加用户体验。代码如下:

static void Main(string[] args)
{
    NewTaskDelegate task 
= newTask;
    IAsyncResult asyncResult 
= task.BeginInvoke(2000nullnull);
 
    
while (!asyncResult.IsCompleted)
    {
        Console.Write(
"*");
        Thread.Sleep(
100);
    }
    
// 由于异步调用已经完成,因此, EndInvoke会立刻返回结果
    int result = task.EndInvoke(asyncResult);           
    Console.WriteLine(result);
}


    上面代码的执行结果如下图所示。



    由于是异步,所以“*”可能会在“任务开始”前输出,如上图所示。


五、使用WaitOne方法等待异步方法执行完成

    
使用WaitOne方法是另外一种判断异步调用是否完成的方法。代码如下:

static void Main(string[] args)
{
    NewTaskDelegate task 
= newTask;
    IAsyncResult asyncResult 
= task.BeginInvoke(2000nullnull);

    
while (!asyncResult.AsyncWaitHandle.WaitOne(100false))
    {
         Console.Write(
"*");              
    }

    
int result = task.EndInvoke(asyncResult);
    Console.WriteLine(result);
}

    WaitOne的第一个参数表示要等待的毫秒数,在指定时间之内,WaitOne方法将一直等待,直到异步调用完成,并发出通知,WaitOne方法才返回true。当等待指定时间之后,异步调用仍未完成,WaitOne方法返回false,如果指定时间为0,表示不等待,如果为-1,表示永远等待,直到异步调用完成。

六、使用回调方式返回结果

    上面介绍的几种方法实际上只相当于一种方法。这些方法虽然可以成功返回结果,也可以给用户一些提示,但在这个过程中,整个程序就象死了一样(如果读者在GUI程序中使用这些方法就会非常明显),要想在调用的过程中,程序仍然可以正常做其它的工作,就必须使用异步调用的方式。下面我们使用GUI程序来编写一个例子,代码如下:

private delegate int MyMethod();
private int method()
{
    Thread.Sleep(
10000);
    
return 100;
}
private void MethodCompleted(IAsyncResult asyncResult)
{
    
if (asyncResult == nullreturn;
    textBox1.Text 
= (asyncResult.AsyncState as 
    MyMethod).EndInvoke(asyncResult).ToString();
}

private void button1_Click(object sender, EventArgs e)
{

    MyMethod my 
= method;
    IAsyncResult asyncResult 
= my.BeginInvoke(MethodCompleted, my);
}

    要注意的是,这里使用了BeginInvoke方法的最后两个参数(如果被调用的方法含有参数的话,这些参数将作为BeginInvoke的前面一部分参数,如果没有参数,BeginInvoke就只有两个参数了)。第一个参数是回调方法委托类型,这个委托只有一个参数,就是IAsyncResult,MethodCompleted方法所示。当method方法执行完后,系统会自动调用MethodCompleted方法。BeginInvoke的第二个参数需要向MethodCompleted方法中传递一些值,一般可以传递被调用方法的委托,如上面代码中的my。这个值可以使用IAsyncResult.AsyncState属性获得。

    由于上面的代码通过异步的方式访问的form上的一个textbox,因此,需要按ctrl+f5运行程序(不能直接按F5运行程序,否则无法在其他线程中访问这个textbox,关于如果在其他线程中访问GUI组件,并在后面的部分详细介绍)。并在form上放一些其他的可视控件,然在点击button1后,其它的控件仍然可以使用,就象什么事都没有发生过一样,在10秒后,在textbox1中将输出100

七、其他组件的BeginXXXEndXXX方法

    在其他的.net组件中也有类似BeginInvokeEndInvoke的方法,如System.Net.HttpWebRequest类的BeginGetResponseEndGetResponse方法,下面是使用这两个方法的一个例子:

private void requestCompleted(IAsyncResult asyncResult)
{
    
if (asyncResult == nullreturn;
    System.Net.HttpWebRequest hwr 
= asyncResult.AsyncState as System.Net.HttpWebRequest;
    System.Net.HttpWebResponse response 
= 
(System.Net.HttpWebResponse)hwr.EndGetResponse(asyncResult);
    System.IO.StreamReader sr 
= new 
System.IO.StreamReader(response.GetResponseStream());
    textBox1.Text 
= sr.ReadToEnd();
}
private delegate System.Net.HttpWebResponse RequestDelegate(System.Net.HttpWebRequest request);

private void button1_Click(object sender, EventArgs e)
{
    System.Net.HttpWebRequest request 
= 
    (System.Net.HttpWebRequest)System.Net.WebRequest.Create(
"http://www.cnblogs.com");
    IAsyncResult asyncResult 
=request.BeginGetResponse(requestCompleted, request);     
}

分享到:
评论

相关推荐

    C# 委托 线程 事件的事例代码

    在C#编程中,委托、线程和事件是三个核心概念,它们对于构建高效、响应式的多任务应用程序至关重要。下面将详细阐述这三个概念及其相互之间的关系,并通过一个实例代码进行说明。 1. 委托(Delegate): 委托在C#中...

    基础委托线程的实现

    在Windows Forms(Winform)应用开发中,委托和线程是两个非常重要的概念,它们能够帮助我们构建出高效、响应迅速的用户界面。本篇将详细介绍基础委托和线程的实现,以及它们如何在Winform应用程序中协同工作。 ...

    Weituo.rar_CSharp 委托_委托_委托 多线程_委托 线程

    《C#中的委托与多线程应用》 在C#编程中,委托是实现多线程和事件处理机制的关键概念。本文将深入探讨委托的原理、使用方式以及它如何与多线程相结合,同时结合“Weituo.rar”提供的示例代码进行分析。 一、委托的...

    委托 线程 访问WINFORM控件

    然而,由于线程安全性的限制,非UI线程不能直接修改UI元素,这时就需要引入委托来安全地从其他线程访问WinForm控件。 委托在.NET中是一种类型安全的函数指针,它可以引用方法并作为参数传递,或者存储在变量中。在...

    C# 委托 线程 事件的实例代码

    不了解 委托 事件 线程 请下载此实例。 此压缩包有4个例子,本人也是看此4个例子中2个例子。学会C#必须懂的委托及事件和2着之间关系,才恍然大悟。 说句不好意思的话,搞C#2年多了,只抄来就用,从没实际了解过,...

    C# winform 防止界面卡住 线程 委托

    为了解决这个问题,我们需要理解并运用线程和委托的概念。本文将详细阐述如何在C# WinForm应用中防止界面卡住,以及线程和委托在其中的作用。 首先,让我们了解一下什么是线程。线程是程序执行的流程,每个进程至少...

    C#多线程委托

    在C#编程中,多线程和委托是两个非常重要的概念,它们对于开发高效、响应式的应用程序至关重要。本文将深入探讨这两个主题,并结合一个简单的Windows Forms应用实例来演示如何使用它们来更新用户界面(UI)。 一、...

    vb.net 委托与非委托对线程的影响

    本文将深入探讨“委托”和“非委托”在子线程与UI线程交互中的作用,以及它们如何影响程序的性能和响应性。 首先,让我们了解什么是委托。在.NET框架中,委托是一种类型安全的函数指针,可以引用方法。它允许我们将...

    Winform(C#)委托+进度条赋值+线程安全(全新)

    本知识点将探讨如何在Winform应用中使用C#实现委托、进度条赋值以及线程安全的操作,这些都是创建高效、用户友好型程序的关键元素。 1. **委托(Delegate)**: 委托在C#中是一种类型,它代表一个方法的引用。它可以...

    c#一个简单的线程实例 委托控制控件

    本实例将探讨如何使用C#创建一个简单的线程,并通过委托来控制UI控件,确保在非UI线程中安全地更新界面。 标题中的“c#一个简单的线程实例 委托控制控件”表明我们将学习如何启动一个新的线程,并使用委托来与UI...

    NET多线程与委托实例

    在.NET框架中,多线程和委托是两个关键的概念,它们在开发高性能、响应迅速的应用程序时发挥着至关重要的作用。本文将详细讲解这两个概念及其实际应用,特别针对解决WinForms应用程序可能出现的卡死问题。 **多线程...

    多线程和委托的简单用法.

    在编程领域,多线程和委托是两个非常重要的概念,特别是在高性能和响应迅速的应用程序开发中。多线程允许程序同时执行多个任务,而委托则是.NET框架中的一个关键特性,它实现了函数对象和事件处理机制。 多线程: ...

    C# 通过委托和线程实现chart控件的实时更新

    C# 通过委托和线程实现 Chart 控件的实时更新 在 C# 编程中,实现 Chart 控件的实时更新是一个常见的需求。为此,我们可以使用委托(Delegate)和线程(Thread)来实现这个功能。在本文中,我们将详细介绍如何使用...

    C#多线程委托带参数

    *********************************************************...两个线程,利用委托更新主线程listBox1控件的内容,并将线程的名字传递过去 ***********************************************************************

    c#多线程利用委托更新控件内容

    C# 多线程利用委托更新控件内容 在 C# 编程中,多线程编程是常见的场景之一,特别是在需要实时更新控件内容的情况下。为了解决多线程之间的数据更新问题,本文将介绍如何使用委托(delegate)来更新控件的内容。 ...

    c# 委托访问listbox多线程操作

    ### C# 委托访问 ListBox 的多线程操作 #### 概述 在 C# 编程语言中,为了确保界面的响应性并避免在执行长时间运行的任务时导致用户界面(UI)冻结,通常会采用多线程技术来处理后台任务。在本案例中,介绍了一种...

    多线程以及委托的应用

    在编程领域,多线程和委托是两种非常重要的概念,特别是在C#这样的.NET环境中,它们被广泛用于构建高效、响应迅速的应用程序。本篇将详细阐述这两个概念及其在实际项目中的应用。 首先,多线程是指在一个应用程序中...

    带参数的委托并且用线程来执行

    ### 带参数的委托与线程操作 在.NET框架中,委托是一种引用类型的数据结构,它可以被用来封装方法的引用。当一个方法被封装到委托变量后,可以通过该委托变量来调用所封装的方法。这使得我们可以在不知道具体方法的...

    多线程委托

    在C#编程中,多线程委托是一种强大的工具,它结合了多线程与委托(Delegate)的概念,允许程序员在不同的线程之间传递方法调用。这篇文章将深入探讨多线程委托及其在跨线程访问中的应用。 首先,理解多线程是至关...

    vb.net多线程委托托管实例.rar Thread委托托管实例

    本重新为vb.net多线程委托托管实例源码,主要有以下功能模块: 1.Thread多线程技术功能模块, 2.3个实例窗体如何进行委托托管及参数传输功能模块 3.告诉你如何屏蔽错误及不屏蔽错误,如何跨窗口运行。程序以最...

Global site tag (gtag.js) - Google Analytics