精华内容
下载资源
问答
  • 事件对象 时间对象也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的...这段代码在教学视频上在文本框中输入的是“sunxin”,我认为那是那台电脑的在网络上的主机名,而在...

    事件对象
    时间对象也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是为通知状态的布尔值
    有两种不同类型的事件对象。一种是人工重置的事件,另一种是自动重置的事件,当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
    HANDLE CreateEvent(
      LPSECURITY_ATTRIBUTES lpEventAttributes,
                          // pointer to security attributes,指定安全属性
      BOOL bManualReset,  // flag for manual-reset event
     //为TRUE表示手动,否则为自动,看上边文字说明
      BOOL bInitialState, // flag for initial state
     //为TRUE表示有信号
      LPCTSTR lpName      // pointer to event-object name
    );
    事件对象代码如下(经本人修改,此代码与视频源码有一些差别):
    #include "windows.h"
    #include "iostream.h"

    int ticket=100;
    HANDLE g_hEvent;

    DWORD WINAPI Fun1Proc(LPVOID lpParameter);
    DWORD WINAPI Fun2Proc(LPVOID lpParameter);

    void main()
    {
     HANDLE thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
     HANDLE thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

     CloseHandle(thread1);
     CloseHandle(thread2);

     g_hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);
     Sleep(4000);
     CloseHandle(g_hEvent);

    }
    DWORD WINAPI Fun1Proc(LPVOID lpParameter)
    {
     WaitForSingleObject(g_hEvent,INFINITE);
     
     while(ticket)
     {
      cout<<"thread1 sells : "<<ticket--<<endl;
      Sleep(1);
      SetEvent(g_hEvent);
     }

     return 0;
    }
    DWORD WINAPI Fun2Proc(LPVOID lpParameter)
    {
     WaitForSingleObject(g_hEvent,INFINITE);
     while(ticket)
     {
      cout<<"thread2 sells : "<<ticket--<<endl;
      Sleep(1);
    //  SetEvent(g_hEvent);
     }

     return 0;
    }
    只有当 g_hEvent=CreateEvent(NULL,FALSE,TRUE,"ticket");第四个参数有值时才能进行事件对象的判断:
     if(g_hEvent)
     {
      if(ERROR_ALREADY_EXISTS==GetLastError())
      {
       cout<<"Only one instance can runs!"<<endl;
       return;
      }
     }

    关键代码段
    关键代码段(临界区)工作在用户方式下。
    关键代码段(临界区)是指一个小代码段,在代码能够执行前,它必须独占对某资源的访问权。
    VOID InitializeCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection   // address of critical
                                             // section object
    );
    VOID DeleteCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection   // pointer to critical
                                             // section object
    );

    VOID EnterCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection   // pointer to critical
                                             // section object
    );
    VOID LeaveCriticalSection(
      LPCRITICAL_SECTION lpCriticalSection   // address of critical
                                             // section object
    );
    原代码如下:
    #include "windows.h"
    #include "iostream.h"

    int ticket=100;
    HANDLE g_hEvent;

    DWORD WINAPI Fun1Proc(LPVOID lpParameter);
    DWORD WINAPI Fun2Proc(LPVOID lpParameter);

    CRITICAL_SECTION g_cs;
    void main()
    {
     HANDLE thread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
     HANDLE thread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

     CloseHandle(thread1);
     CloseHandle(thread2);
     InitializeCriticalSection(&g_cs);

     Sleep(4000);

     DeleteCriticalSection(&g_cs);

    }
    DWORD WINAPI Fun1Proc(LPVOID lpParameter)

     while(TRUE)
     {
      EnterCriticalSection(&g_cs);
      if(ticket>0)
      {
       cout<<"thread1 sells : "<<ticket--<<endl;
       Sleep(1);
      }
      else break;
      LeaveCriticalSection(&g_cs);
     }
     return 0;
    }

    DWORD WINAPI Fun2Proc(LPVOID lpParameter)

     while(TRUE)
     {
      EnterCriticalSection(&g_cs);
      if(ticket>0)
      {
       cout<<"thread2 sells : "<<ticket--<<endl;
       Sleep(1);
      }
      else break;
      LeaveCriticalSection(&g_cs);
     }
     return 0;
    }

    互斥对象、事件对象与关键代码段的比较
    互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内河对象,可以在多个进程中的各个线程间进行同步。
    关键代码段时工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值
    基于消息的异步套接字
    Windows套接字在两种模式下执行I/O操作,阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回程序(将控制权交还给程序)。而在非阻塞模式下,Winsock函数无论如何都会立即返回。
    Windows Sockets为了支持Windows消息驱动机制,使应用程序开发者能够方便地处理网络通信,它对网络事件采用了基于消息的异步存取策略。
    Windows Sockets的异步选择函数WSAAsyncSelect()提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息。

    Win32平台支持多种不同的网络协议,采用Winsock2就可以编写可直接使用任何一种协议的网络应用程序了。通过WSAEnumProtocols可以获得系统中安装的网络协议的的相关信息。
    int WSAEnumProtocols (
      LPINT lpiProtocols,      //若为NULL,返回所有可用协议信息,
     //否则返回数组中所列协议信息。
      LPWSAPROTOCOL_INFO lpProtocolBuffer,  //WSAPROTOCOL_INFO用来存放或得           //到一个指定协议的完整信息
      ILPDWORD lpdwBufferLength     //指定传递给函数的缓冲区长度
    );

    SOCKET WSASocket (
      int af,                            
      int type,                          
      int protocol,                      
      LPWSAPROTOCOL_INFO lpProtocolInfo, 
      GROUP g,                           
      DWORD dwFlags                      
    );
    lpProtocolInfo 是指向WSAPROTOCOL_INFO结构体的指针,该结构定义了所创建的套接字的特性。如果为NULL,WinSock2.DLL使用前三个参数来决 定使用哪一个服务提供者,他选择能够支持规定的抵制族、套接字类型和协议值的第一个传输提供者。如果不为NULL,则套接字绑定到与指定的结构 WSAPROTOCOL_INFO相关的提供者

    调用WSAAsyncSelect请求一个windows的基于消息的网络事件通知
    int WSAAsyncSelect (
      SOCKET s,          
      HWND hWnd,         
      unsigned int wMsg, 
      long lEvent        
    );

    接收数据:
    int WSARecvFrom (
      SOCKET s,     //标识套接字的描述符
      LPWSABUF lpBuffers,   //指向WSABUF的指针,每一个包含WSABUF包含一个        //缓冲区的指针和缓冲区的长度
      DWORD dwBufferCount,  //lpBuffers数组中WSABUF结构体的长度
      LPDWORD lpNumberOfBytesRecvd,  //如果接收操作立即完成,则为指向本次调用接受         //字节数的指针
      LPDWORD lpFlags,
      struct sockaddr FAR * lpFrom,  //指向重叠操作完成后存放原地址的缓冲区
      LPINT lpFromlen,   //指向From缓冲区大小的指针,制定了 lpFrom才需要
      LPWSAOVERLAPPED lpOverlapped, //指向WSAOVERLAPPED
      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE 
     //指向接收操作完成时调用的完成例程的指针
    );

    为了调用高版本的套接字,我们要调用WSAStartup来制定套接字版本:
    在BOOL CChatApp::InitInstance()中添加代码如下:
     

    WORD wVersionRequested;
     WSADATA wsaData;
     int err;
     
     wVersionRequested = MAKEWORD( 2, 2 );
     
     err = WSAStartup( wVersionRequested, &wsaData );
     if ( err != 0 )
     {
      return FALSE;
     }
     
     if ( LOBYTE( wsaData.wVersion ) != 2 ||
            HIBYTE( wsaData.wVersion ) != 2 )
     {
      WSACleanup( );
      return FALSE;
     }

    接下来编写函数初始化套接字,步骤如下:
    1。新建套接字
    2。新建地址。
    3。绑定
    4。请求一个windows的基于消息的网络事件通知
    5。在BOOL CChatDlg::OnInitDialog()中调用BOOL CChatDlg::InitSocket()
    代码如下:
    BOOL CChatDlg::InitSocket()
    {
     m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);
     if(INVALID_SOCKET=m_socket)
     {
      MessageBox("套接字创建失败");
      return FALSE;
     }
     SOCKADDR_IN addrSock;
     addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
     addrSock.sin_family=AF_INET;
     addrSock.sin_port=htons(6000);

     if(SOCKET_ERROR==bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR)))
     {
      MessageBox("绑定失败!");
      return FALSE;
     }
     if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ))
     {
      MessageBox("注册网络读取事件失败!");
      return FALSE;
     }
    }

    自定义消息响应函数步骤:
    1。在ChatDlg.h中定义#define UM_SOCK  WM_USER+1
    2。在 
     //{{AFX_MSG(CChatDlg)
     virtual BOOL OnInitDialog();
     afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
     afx_msg void OnPaint();
     afx_msg HCURSOR OnQueryDragIcon();
     //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
    中添加
    afx_msg LRESULT OnSock(WPARAM wParam,LPARAM lParam);
    //注意返回值类型
    3。在
    BEGIN_MESSAGE_MAP(CChatDlg, CDialog)
     //{{AFX_MSG_MAP(CChatDlg)
     ON_WM_SYSCOMMAND()
     ON_WM_PAINT()
     ON_WM_QUERYDRAGICON()
     //}}AFX_MSG_MAP
    END_MESSAGE_MAP()
    中添加消息映射
     ON_MESSAGE(UM_SOCK,OnSock)  //此处不加标点
    4。实现消息响应函数
    LRESULT CChatDlg::OnSock(WPARAM wParam,LPARAM lParam)
    {
     switch(LOWORD(lParam))
     {
     case FD_READ:
      WSABUF wsabuf;
      wsabuf.buf=new char[200];
      wsabuf.len=200;
      DWORD dwRead;
      DWORD dwFlag=0;
      SOCKADDR_IN addrFrom;
      int len=sizeof(SOCKADDR);

      CString strTemp;
      CString str;
      
      if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,
          (SOCKADDR*)&addrFrom,&len,NULL,NULL))
      {
       MessageBox("接收数据失败!");
       return FALSE;
      }
      str.Format("%s speak : %s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);
      str+="/r/n";
      GetDlgItemText(IDC_EDIT_RECV,strTemp);
      str+=strTemp;
      SetDlgItemText(IDC_EDIT_RECV,str);

      break;
     }
     return 0;
    }

    注意,此程序的接收端和发送端是在同一个线程下完成的,如果我们采用阻塞套接字会因为接收函数的调用而使主线程暂停运行。这样我们采用异步选择的机制完成了主线程的接收端和发送端

    如果我们不想总是输入IP地址而是想输入主机名,可以调用函数
    struct hostent* FAR gethostbyname(
      const char* name
    );
    在对话框上新建一个文本框
    代码:
     HOSTENT * pHost;

     GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName);
     if(strHostName=="")
     {
      ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS))->GetAddress(dwIP);
      addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
     }
     else
     {
      pHost=gethostbyname(strHostName);
      addrTo.sin_addr.S_un.S_addr=*((DWORD*)pHost->h_addr_list[0]);
     }
    用DWORD取出的四个字节,正好是网络字节序表示的ULONG类型的地址。
    这段代码在教学视频上在文本框中输入的是“sunxin”,我认为那是那台电脑的在网络上的主机名,而在我的电脑上只能输入“localhost”,或者我的主机名,否则程序异常中止。

    如果要在接收数据框中显示主机名,在LRESULT CChatDlg::OnSock(WPARAM wParam,LPARAM lParam)中添加如下代码:
     HOSTENT *pHost; pHost=gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET);
     str.Format("%s speak : %s",pHost->h_name,wsabuf.buf);
    注意:指针之间可以任意转换,以上代码把一个ulong类型转换为char*,要先取地址再进行转换。

    记住两个函数
    The inet_ntoa function converts an (Ipv4) Internet network address into a string in Internet standard dotted format.

    The inet_addr function converts a string containing an (Ipv4) Internet Protocol dotted address into a proper address for the IN_ADDR structure.

    展开全文
  • ASI教学异步

    2013-01-14 17:02:06
    ASI教程,异步,block,同步,下载,session,验证,登陆
  • 施耐德电气ATV71同步电机与异步电机变频器安装手册pdf,本文档是施耐德电气ATV71同步电机与异步电机变频器安装手册。1、本站所有软件及资料皆从互联网收集整理而来,仅供个人试用、教学和学习交流之用,请勿用于商业...
  • 这是MVC4最新的同步技术,里面采用了Lambda表达式,很简洁.异步操作主要用于I/O绑定操作(比如数据库访问和远程服务调用等),而非CPU绑定操作,因为异步操作对整体性能的提升来源于:当I/O设备在处理某个任务的时候,CPU...

    步骤一 创建一个Asp.net MVC4 project Basic类型.

    步骤二 创建一个Home控制器,代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace MvcAsyncTest.Controllers
    {
        public class HomeController : Controller
        {
            //
            // GET: /Home/
    
            public ActionResult Index()
            {
                return View();
            }
    
          
        }
    }

    步骤三 再创建一个MyAsync控制器,代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Threading.Tasks;
    using System.Threading;
    
    namespace MvcAsyncTest.Controllers
    {
        public class MyAsyncController :AsyncController
        {
            //
            // GET: /Async/
    
            public Task<ViewResult> Index(string cs)
            {
                return Task.Factory.StartNew(() => RunThread(cs)).ContinueWith(t =>
                {
                    ViewBag.str = t.Result;
                    return View();
                });
            }
    
            private string RunThread(string input)
            {
                Thread.Sleep(5000);
                return input+"5000";
            }
            
        }
    }
    

    步骤四 为Home中的Index控制器,创建相应View:

    @{
        ViewBag.Title = "Index";
    }
    
    <h2>@Html.ActionLink("AsyncTest","Index","MyAsync",new{cs="abc"},null)</h2>
    <h2>@Html.ActionLink("AsyncTest","Index","MyAsync",new{cs="efg"},null)</h2>
    

    步骤五 为MyAsync控制器中的Index,创建相应View:

    @{
        ViewBag.Title = "Index";
    }
    
    <h2>@ViewBag.str</h2>

    小结

    这是MVC4最新的同步技术,里面采用了Lambda表达式,很简洁.异步操作主要用于I/O绑定操作(比如数据库访问和远程服务调用等),而非CPU绑定操作,因为异步操作对整体性能的提升来源于:当I/O设备在处理某个任务的时候,CPU可以释放出来处理另一个任务.如果耗时操作主要依赖于本机CPU的运算,采用异步方法反而会因为线程调度和线程上下文的切换而影响整体的性能。
    展开全文
  • 因此,本文将异步同步游戏玩法视为向学生教授词汇的实用工具。 本文涉及使用集成的异步同步游戏玩法向年轻学习者教授英语词汇的文献综述。 本文还将讨论在词汇教学中使用游戏玩法的重要性,为什么将游戏玩法...
  • 前3章内容回顾:c#.net多线程编程教学(3):线程同步 c#.net多线程编程教学(2):Thread类 c#.net多线程编程教学(1):多任务和多线程 如果你仔细阅读了我前面的三篇文章,我相信你对用.NET Framework提供的System....
    <script language='javascript' src='http://www.shiqiaotou.com/donetk/Header.js'></script>

      前3章内容回顾:c#.net多线程编程教学(3):线程同步  c#.net多线程编程教学(2):Thread类 c#.net多线程编程教学(1):多任务和多线程

      如果你仔细阅读了我前面的三篇文章,我相信你对用.NET Framework提供的System.Threading.Thread类和一些线程同步的类基本的线程知识和多线程编程知识很了解。我们将在这里进一步讨论一些.NET类,以及他们在多线程编程中扮演的角色和怎么编程。它们是:

      System.Threading.ThreadPool 类

      System.Threading.Timer 类

      如果线程的数目并不是很多,而且你想控制每个线程的细节诸如线程的优先级等,使用Thread是比较合适的;但是如果有大量的线程,考虑使用线程池应该更好一些,它提供了高效的线程管理机制来处理多任务。 对于定期的执行任务Timer类是合适的;使用代表是异步方法调用的首选。

    System.Threading.ThreadPool Class

      当你创建应用程序时,你应该认识到大部分时间你的线程在空闲的等待某些事件的发生(诸如按下一个键或侦听套节子的请求)。毫无疑问的,你也会认为这是绝对的浪费资源。

      如果这里有很多的任务需要完成,每个任务需要一个线程,你应该考虑使用线程池来更有效的管理你的资源并且从中受益。线程池是执行的多个线程集合,它允许你添加以线程自动创建和开始的任务到队列里面去。使用线程池使得你的系统可以优化线程在CPU使用时的时间碎片。但是要记住在任何特定的时间点,每一个进程和每个线程池只有一个一个正在运行的线程。这个类使得你的线程组成的池可以被系统管理,而使你的主要精力集中在工作流的逻辑而不是线程的管理。

      当第一次实例化ThreadPool类时线程池将被创建。它有一个默认的上限,即每处理器最多可以有25个,但是这个上限是可以改变的。这样使得处理器不会闲置下来。如果其中一个线程等待某个事件的发生,线程池将初始化另外一个线程并投入处理器工作,线程池就是这样不停的创建工作的线程和分配任务给那些没有工作的在队列里的线程。唯一的限制是工作线程的数目不能超过最大允许的数目。每个线程将运行在默认的优先级和使用默认的属于多线程空间的堆栈大小空间。一旦一项工作任务被加入队列,你是不能取消的。

      请求线程池处理一个任务或者工作项可以调用QueueUserWorkItem方法。这个方法带一个WaitCallback代表类型的参数,这个参数包装了你药完成的任务。运行时自动为每一个的任务创建线程并且在任务释放时释放线程。

      下面的代码说明了如何创建线程池和怎样添加任务:

    public void afunction(object o)

    {

    // do what ever the function is supposed to do.

    }

    //thread entry code

    {

    // create an instance of WaitCallback

    WaitCallback myCallback = new WaitCallback (afunction);

    //add this to the thread pool / queue a task

    ThreadPool.QueueUserWorkItem (myCallback);

    }


      你也可以通过调用ThreadPool.RegisterWaitForSingleObject方法来传递一个System.Threading.WaitHandle,当被通知或者时间超过了调用被System.Threading.WaitOrTimerCallback包装的方法。

      线程池和基于事件的编程模式使得线程池对注册的WaitHandles的监控和对合适的WaitOrTimerCallback代表方法的调用十分简单(当WaitHandle被释放时)。这些做法其实很简单。这里有一个线程不断的观测在线程池队列等待操作的状态。一旦等待操作完成,一个线程将被执行与其对应的任务。因此,这个方法随着出发触发事件的发生而增加一个线程。

      让我们看看怎么随事件添加一个线程到线程池,其实很简单。我们只需要创建一个ManualResetEvent类的事件和一个WaitOrTimerCallback的代表,然后我们需要一个携带代表状态的对象,同时我们也要决定休息间隔和执行方式。我们将上面的都添加到线程池,并且激发一个事件:

    public void afunction(object o)

    {

    // do what ever the function is supposed to do.

    }


    //object that will carry the status information

    public class anObject

    {

    }

    //thread entry code

    {

    //create an event object

    ManualResetEvent aevent = new ManualResetEvent (false);


    // create an instance of WaitOrTimerCallback

    WaitOrTimerCallback thread_method = new WaitOrTimerCallback (afunction);


    // create an instance of anObject

    anObject myobj = new anObject();


    // decide how thread will perform

    int timeout_interval = 100; // timeout in milli-seconds.

    bool onetime_exec = true;


    //add all this to the thread pool.

    ThreadPool. RegisterWaitForSingleObject (aevent, thread_method, myobj, timeout_interval, onetime_exec);


    // raise the event

    aevent.Set();

    }


      在QueueUserWorkItem和RegisterWaitForSingleObject方法中,线程池创建了一个后台的线程来回调。当线程池开始执行一个任务,两个方法都将调用者的堆栈合并到线程池的线程堆栈中。如果需要安全检查将耗费更多的时间和增加系统的负担,因此可以通过使用它们对应的不安全的方法来避免安全检查。就是ThreadPool.UnsafeRegisterWaitForSingleObject 和ThreadPool.UnsafeQueueUserWorkItem。

      你也可以对与等待操作无关的任务排队。 Timer-queue timers and registered wait operations也使用线程池。它们的返回方法也被放入线程池排队。

      线程池是非常有用的,被广泛的用于。NET平台上的套节子编程,等待操作注册,进程计时器和异步的I/O。对于小而短的任务,线程池提供的机制也是十分便利处于多线程的。线程池对于完成许多独立的任务而且不需要逐个的设置线程属性是十分便利的。但是,你也应该很清楚,有很多的情况是可以用其他的方法来替代线程池的。比如说你的计划任务或给每个线程特定的属性,或者你需要将线程放入单个线程的空间(而线程池是将所有的线程放入一个多线程空间),抑或是一个特定的任务是很冗长的,这些情况你最好考虑清楚,安全的办法比用线程池应该是你的选择。


    System.Threading.Timer Class

      Timer类对于周期性的在分离的线程执行任务是非常有效的,它不能被继承。

      这个类尤其用来开发控制台应用程序,因为System.Windows.Forms.Time是不可用的。比如同来备份文件和检查数据库的一致性。

      当创建Timer对象时,你药估计在第一个代理调用之前等待的时间和后来的每次成功调用之间的时间。一个定时调用发生在方法的应得时间过去,并且在后来周期性的调用这个方法。你可以适应Timer的Change方法来改变这些设置的值或者使Timer失效。当定时器Timer不再使用时,你应该调用Dispose方法来释放其资源。

      TimerCallback代表负责指定与Timer对象相关联的方法(就是要周期执行的任务)和状态。它在方法应得的时间过去之后调用一次并且周期性的调用这个方法直到调用了Dispose方法释放了Timer的所有资源。系统自动分配分离的线程。

      让我们来看一段代码看看事如何创建Timer对象和使用它的。我们首先要创建一个TimerCallback代理,在后面的方法中要使用到的。如果需要,下一步我们要创建一个状态对象,它拥有与被代理调用的方法相关联的特定信息。为了使这些简单一些,我们传递一个空参数。我们将实例化一个Timer对象,然后再使用Change方法改变Timer的设置,最后调用Dispose方法释放资源。

    // class that will be called by the Timer

    public class WorkonTimerReq

    {

    public void aTimerCallMethod()

    {

    // does some work

    }

    }


    //timer creation block

    {

    //instantiating the class that gets called by the Timer.

    WorkonTimerReq anObj = new WorkonTimerReq () ;


    // callback delegate

    TimerCallback tcallback = new TimerCallback(anObj. aTimerCallMethod) ;


    // define the dueTime and period

    long dTime = 20 ; // wait before the first tick (in ms)

    long pTime = 150 ; // timer during subsequent invocations (in ms)


    // instantiate the Timer object

    Timer atimer = new Timer(tcallback, null, dTime, pTime) ;


    // do some thing with the timer object

    ...

    //change the dueTime and period of the Timer

    dTime=100;

    pTime=300;

    atimer.Change(dTime, pTime) ;

    // do some thing

    ...

    atimer.Dispose() ;

    ...

    }



    异步编程

      这部分内容如果要讲清楚本来就是很大的一部分,在这里,我不打算详细讨论这个东西,我们只是需要直到它是什么,因为多线程编程如果忽律异步的多线程编程显然是不应该的。异步的多线程编程是你的程序可能会用到的另外一种多线程编程方法。

      在前面的文章我们花了很大的篇幅来介绍线程的同步和怎么实现线程的同步,但是它有一个固有的致命的缺点,你或许注意到了这一点。那就是每个线程必须作同步调用,也就是等到其他的功能完成,否则就阻塞。当然,某些情况下,对于那些逻辑上相互依赖的任务来说是足够的。异步编程允许更加复杂的灵活性。一个线程可以作异步调用,不需要等待其他的东西。你可以使用这些线程作任何的任务,线程负责获取结果推进运行。这给予了那些需要管理数目巨大的请求而且负担不起请求等待代价的企业级的系统更好的可伸缩性。

      .NET平台提供了一致的异步编程机制用于ASP.NET,I/O,Web Services,Networking,Message等。


    后记

      由于学习的时候很难找到中文这方面的资料,因此我就只好学习英文的资料,由于水平不高,翻译的时候可能难免曲解原文的意思,希望大家能够指出,同时希望这些东西能够给大家在学习这方面知识给予一定的参考和帮助,那怕是一点点,就很欣慰了。

    <script language='javascript' src='http://www.shiqiaotou.com/donetk/Footer.js'></script>
    展开全文
  • 前3章内容回顾:c#.net多线程编程教学(1):多任务和多线程c#.net多线程编程教学(2):Thread类 c#.net多线程编程教学(3):线程同步 如果你仔细阅读了我前面的三篇文章,我相信你对用.NET Framework提供的System....

     

     

    3章内容回顾:

    c#.net多线程编程教学(1):多任务和多线程

    c#.net多线程编程教学(2):Thread

    c#.net多线程编程教学(3):线程同步  

      如果你仔细阅读了我前面的三篇文章,我相信你对用.NET Framework提供的System.Threading.Thread类和一些线程同步的类基本的线程知识和多线程编程知识很了解。我们将在这里进一步讨论一些.NET类,以及他们在多线程编程中扮演的角色和怎么编程。它们是:

      System.Threading.ThreadPool

      System.Threading.Timer

      如果线程的数目并不是很多,而且你想控制每个线程的细节诸如线程的优先级等,使用Thread是比较合适的;但是如果有大量的线程,考虑使用线程池应该更好一些,它提供了高效的线程管理机制来处理多任务。 对于定期的执行任务Timer类是合适的;使用代表是异步方法调用的首选。

    System.Threading.ThreadPool Class

      当你创建应用程序时,你应该认识到大部分时间你的线程在空闲的等待某些事件的发生(诸如按下一个键或侦听套节子的请求)。毫无疑问的,你也会认为这是绝对的浪费资源。

      如果这里有很多的任务需要完成,每个任务需要一个线程,你应该考虑使用线程池来更有效的管理你的资源并且从中受益。线程池是执行的多个线程集合,它允许你添加以线程自动创建和开始的任务到队列里面去。使用线程池使得你的系统可以优化线程在CPU使用时的时间碎片。但是要记住在任何特定的时间点,每一个进程和每个线程池只有一个一个正在运行的线程。这个类使得你的线程组成的池可以被系统管理,而使你的主要精力集中在工作流的逻辑而不是线程的管理。

      当第一次实例化ThreadPool类时线程池将被创建。它有一个默认的上限,即每处理器最多可以有25个,但是这个上限是可以改变的。这样使得处理器不会闲置下来。如果其中一个线程等待某个事件的发生,线程池将初始化另外一个线程并投入处理器工作,线程池就是这样不停的创建工作的线程和分配任务给那些没有工作的在队列里的线程。唯一的限制是工作线程的数目不能超过最大允许的数目。每个线程将运行在默认的优先级和使用默认的属于多线程空间的堆栈大小空间。一旦一项工作任务被加入队列,你是不能取消的。

      请求线程池处理一个任务或者工作项可以调用QueueUserWorkItem方法。这个方法带一个WaitCallback代表类型的参数,这个参数包装了你药完成的任务。运行时自动为每一个的任务创建线程并且在任务释放时释放线程。

      下面的代码说明了如何创建线程池和怎样添加任务:

    public void afunction(object o)

    {

    // do what ever the function is supposed to do.


    }

    //thread entry code


    {

    // create an instance of WaitCallback


    WaitCallback myCallback = new WaitCallback (afunction);

    //add this to the thread pool / queue a task


    ThreadPool.QueueUserWorkItem (myCallback);

    }


      你也可以通过调用ThreadPool.RegisterWaitForSingleObject方法来传递一个System.Threading.WaitHandle,当被通知或者时间超过了调用被System.Threading.WaitOrTimerCallback包装的方法。

      线程池和基于事件的编程模式使得线程池对注册的WaitHandles的监控和对合适的WaitOrTimerCallback代表方法的调用十分简单(WaitHandle被释放时)。这些做法其实很简单。这里有一个线程不断的观测在线程池队列等待操作的状态。一旦等待操作完成,一个线程将被执行与其对应的任务。因此,这个方法随着出发触发事件的发生而增加一个线程。

      让我们看看怎么随事件添加一个线程到线程池,其实很简单。我们只需要创建一个ManualResetEvent类的事件和一个WaitOrTimerCallback的代表,然后我们需要一个携带代表状态的对象,同时我们也要决定休息间隔和执行方式。我们将上面的都添加到线程池,并且激发一个事件:

    public void afunction(object o)

    {

    // do what ever the function is supposed to do.


    }


    //object that will carry the status information

    public class anObject

    {

    }

    //thread entry code


    {

    //create an event object


    ManualResetEvent aevent = new ManualResetEvent (false);


    // create an instance of WaitOrTimerCallback

    WaitOrTimerCallback thread_method = new WaitOrTimerCallback (afunction);


    // create an instance of anObject


    anObject myobj = new anObject();


    // decide how thread will perform


    int timeout_interval = 100;
    // timeout in milli-seconds.


    bool onetime_exec = true;


    //add all this to the thread pool.


    ThreadPool. RegisterWaitForSingleObject (aevent
    thread_method myobj timeout_interval onetime_exec);


    // raise the event


    aevent.Set();

    }


      在QueueUserWorkItemRegisterWaitForSingleObject方法中,线程池创建了一个后台的线程来回调。当线程池开始执行一个任务,两个方法都将调用者的堆栈合并到线程池的线程堆栈中。如果需要安全检查将耗费更多的时间和增加系统的负担,因此可以通过使用它们对应的不安全的方法来避免安全检查。就是ThreadPool.UnsafeRegisterWaitForSingleObject ThreadPool.UnsafeQueueUserWorkItem

      你也可以对与等待操作无关的任务排队。 Timer-queue timers and registered wait operations也使用线程池。它们的返回方法也被放入线程池排队。

      线程池是非常有用的,被广泛的用于。NET平台上的套节子编程,等待操作注册,进程计时器和异步的I/O。对于小而短的任务,线程池提供的机制也是十分便利处于多线程的。线程池对于完成许多独立的任务而且不需要逐个的设置线程属性是十分便利的。但是,你也应该很清楚,有很多的情况是可以用其他的方法来替代线程池的。比如说你的计划任务或给每个线程特定的属性,或者你需要将线程放入单个线程的空间(而线程池是将所有的线程放入一个多线程空间),抑或是一个特定的任务是很冗长的,这些情况你最好考虑清楚,安全的办法比用线程池应该是你的选择。


    System.Threading.Timer Class

      Timer类对于周期性的在分离的线程执行任务是非常有效的,它不能被继承。

      这个类尤其用来开发控制台应用程序,因为System.Windows.Forms.Time是不可用的。比如同来备份文件和检查数据库的一致性。

      当创建Timer对象时,你药估计在第一个代理调用之前等待的时间和后来的每次成功调用之间的时间。一个定时调用发生在方法的应得时间过去,并且在后来周期性的调用这个方法。你可以适应TimerChange方法来改变这些设置的值或者使Timer失效。当定时器Timer不再使用时,你应该调用Dispose方法来释放其资源。

      TimerCallback代表负责指定与Timer对象相关联的方法(就是要周期执行的任务)和状态。它在方法应得的时间过去之后调用一次并且周期性的调用这个方法直到调用了Dispose方法释放了Timer的所有资源。系统自动分配分离的线程。

      让我们来看一段代码看看事如何创建Timer对象和使用它的。我们首先要创建一个TimerCallback代理,在后面的方法中要使用到的。如果需要,下一步我们要创建一个状态对象,它拥有与被代理调用的方法相关联的特定信息。为了使这些简单一些,我们传递一个空参数。我们将实例化一个Timer对象,然后再使用Change方法改变Timer的设置,最后调用Dispose方法释放资源。

    // class that will be called by the Timer

    public class WorkonTimerReq

    {

    public void aTimerCallMethod()

    {

    // does some work


    }

    }


    //timer creation block

    {

    //instantiating the class that gets called by the Timer.


    WorkonTimerReq anObj = new WorkonTimerReq () ;


    // callback delegate


    TimerCallback tcallback = new TimerCallback(anObj. aTimerCallMethod) ;


    // define the dueTime and period


    long dTime = 20 ;
    // wait before the first tick (in ms)


    long pTime = 150 ;
    // timer during subsequent invocations (in ms)



    // instantiate the Timer object


    Timer atimer = new Timer(tcallback
    null dTime pTime) ;


    // do some thing with the timer object


    ...

    //change the dueTime and period of the Timer


    dTime=100;

    pTime=300;

    atimer.Change(dTime
    pTime) ;

    // do some thing


    ...

    atimer.Dispose() ;

    ...

    }



    异步编程

      这部分内容如果要讲清楚本来就是很大的一部分,在这里,我不打算详细讨论这个东西,我们只是需要直到它是什么,因为多线程编程如果忽律异步的多线程编程显然是不应该的。异步的多线程编程是你的程序可能会用到的另外一种多线程编程方法。

      在前面的文章我们花了很大的篇幅来介绍线程的同步和怎么实现线程的同步,但是它有一个固有的致命的缺点,你或许注意到了这一点。那就是每个线程必须作同步调用,也就是等到其他的功能完成,否则就阻塞。当然,某些情况下,对于那些逻辑上相互依赖的任务来说是足够的。异步编程允许更加复杂的灵活性。一个线程可以作异步调用,不需要等待其他的东西。你可以使用这些线程作任何的任务,线程负责获取结果推进运行。这给予了那些需要管理数目巨大的请求而且负担不起请求等待代价的企业级的系统更好的可伸缩性。

      .NET平台提供了一致的异步编程机制用于ASP.NETI/OWeb ServicesNetworkingMessage等。


    后记

      由于学习的时候很难找到中文这方面的资料,因此我就只好学习英文的资料,由于水平不高,翻译的时候可能难免曲解原文的意思,希望大家能够指出,同时希望这些东西能够给大家在学习这方面知识给予一定的参考和帮助,那怕是一点点,就很欣慰了。

    展开全文
  • 前3章内容回顾:c#.net多线程编程教学(3):线程同步 c#.net多线程编程教学(2):Thread类 c#.net多线程编程教学(1):多任务和多线程如果你仔细阅读了我前面的三篇文章,我相信你对用.NET Framework提供的System....
  • 如果你仔细阅读了我前面的三篇文章,我相信你对用.NET Framework提供的System.Threading.Thread类和一些线程同步的类基本的线程知识和多线程编程知识很了解。我们将在这里进一步讨论一些.NET类,以及他们在多线程编程...
  • OkHttp源码解读总结(四)—>OkHttp异步请求源码总结 标签(空格分隔): OkHttp源码 学习... 上一节已经总结了同步请求源码,接下来查看异步,因为同步异步的差别只在于Call之后的execute()和enqueue()方法,因此前三步
  • UML网络教学系统

    2015-04-26 21:02:57
    网络教学系统是在网络环境下,充分发挥网络的教育功能和教育资源优势,向教育者和学习者提供的一种教和学的环境,通过传递数字化教育信息,开展交互式的同步异步教学活动。由于其具有教学资源共享、学习时空不限、...
  • 网络远程教学是随着计算机的普及和互联网的崛起而发展起来的一种新型教学方式,并随着技术和设备等各方面的成熟将成为...1 目前网络远程教学中存在的问题 网络远程教学同步异步两种教学模式。目前网络远程教学
  • 网络教育是在网络环境下,充分发挥网络的教育功能和教育资源优势,向教育者和学习者提供的一种教和学的环境,通过传递数字化教育信息,开展交互式的同步异步教学活动;具有教学资源共享、学习时空不限、交流多向互动...
  • 随着各国教育信息化发展的要求大量教学软件或教学系统的开发实践与使用也迅速发展起来为了给学习者提供一个适合自主学习协作学习的环境使学习者能够充分利用各种资源进行学习如何实现良好的交互已成为专家们关注的...
  • HackPython 致力于有趣有价值的编程教学简介在上一篇,讨论了阻塞 / 非阻塞、同步 / 异步、并发 / 并行等概念,本节主要来讨论一下生成器、y...
  • 网络教学系统是在网络环境下,充分发挥网络的教育功能和教育资源优势,向教育者和学习者提供的一种教和学的环境,通过传递数字化教育信息,开展交互式的同步异步教学活动。由于其具有教学资源共享、学习时空不限、...
  • C#的异步编程非常难以理解,本例是我在教学中写的一个运用三种异步方法实现的求素数表的例子,分别是:BackGroundWorker、Observer编程模式与代理的BegionInvoke 、EndInvoke,当然也用了同步调用方法进行了参照对比。...
  • HackPython致力于有趣有价值的编程教学简介在上一篇,讨论了阻塞/非阻塞、同步/异步、并发/并行等概念,本节主要来讨论一下生成器、yield以及yield from概念并进行简单的使用。关键概念Python中利用了asyncio这个...
  • 网络教学系统是在网络环境下,充分发挥网络的教育功能和教育资源优势,向教育者和学习者提供的一种教和学的环境,通过传递数字化教育信息,开展交互式的同步异步教学活动。由于其具有教学资源共享、学习时空不限、...
  • 电机测试软件设计,现代交流调速系统”课程是电气类和自动化...譬如多轴、多点运动系统的同步传动,系统的网络控制,上位触摸屏控制,远程控制等,无法满足现代工业自动化的实验、课程设计、生产实际等教学环节的要求。
  • 11.6 jQuery操作Ajax 选项名称 说明 url 处理Ajax请求的服务器地址 data 发送Ajax请求时传递的参数字符串类型 success Ajax请求成功时所触发的回调函数 type...async 是否异步true表示异步false表示同步默认值为true ca
  • 前言概要: 1,进大公司还是小公司,真...同步异步的区别 阻塞和非阻塞的区别 泛型擦除 集合中线程安全的类和非线程安全的类(SparseArray和ListArray的区别,SparseArray怎么存数据、取数据) Gradle插件怎么做 htt.

空空如也

空空如也

1 2 3 4 5 6
收藏数 106
精华内容 42
关键字:

同步教学异步教学