精华内容
下载资源
问答
  • window网络编程六大模型

    千次阅读 2013-07-10 23:00:03
    一个socket模式简单的决定当以一个socket来调用Winsock函数时它们的行为。另一方面,一个socket I/O模型描述了应用程序如何管理和处理在socket上的输入输出。  Winsock分为两种socket模式:阻塞和

    Winsock I/O方法

            Winsock以socket模型和socket I/O模型来控制如何在一个socket处理输入/输出。一个socket模式简单的决定当以一个socket来调用Winsock函数时它们的行为。另一方面,一个socket I/O模型描述了应用程序如何管理和处理在socket上的输入输出。

            Winsock分为两种socket模式:阻塞和非阻塞。接下来将详细描述这些模式并举例说明一个应用程序如何使用它们来管理I/O。在后面,你将看到,windows提供了一些有趣的I/O模型,来帮助应用程序管理在一个或多个socket上的同时异步通信:阻塞、select、WSAAsyncSelect、WSAEventSelect、overlapped I/O、完成端口。在结尾部分,我们将回顾各种socket模型和I/O模型的优缺点,以帮助你确定哪种模型最适合你的应用。

             在windows NT或之后的版本支持上述所有的I/O模型。

    Sock Modes

            windows 套接字以两种模式来执行I/O操作:阻塞和非阻塞。在阻塞模式下,winsock函数调用I/O操作函数 --- 比如send和recv --- 函数将等待直到操作完成才会返回到调用程序。在非阻塞模式中,winsock函数将立即返回。

    Blocking Mode

             在一个阻塞的socket上调用的所有的winsockAPI都有这样的行为 --- 阻塞一定时间。大多数Winsock应用程序都遵循生产-消费者模型,在这样的模型中,应用程序读(或写)一定字节数的数据并且在这些数据上执行一些计算。如下的代码片段演示了这种模型:

    SOCKET  sock;
    char    buff[256];
    int     done = 0,
            nBytes;
    
    ...
    
    while(!done)
    {
        nBytes = recv(sock, buff, 65);
        if (nBytes == SOCKET_ERROR)
         {
            printf("recv failed with error %d\n",
                WSAGetLastError());
            Return;
        }
        DoComputationOnData(buff);
    }
    

            这段代码的问题是,如果没有数据可读,recv将可能永远不会返回,因为声明表示recv只有在从系统输入缓冲中读了一些字节的数据后才会返回。一些程序员可能忍不住想要窥视系统缓冲中有多少数据,这可以通过以MSG_PEEK调用recv(MSG_PEEK 查看当前数据。数据将被复制到缓冲区中,但并不从输入队列中删除),或者以FIONREAD选项调用ioctlsocket()(返回缓冲中有多少数据)。偷窥数据而没有真正的读取被认为是不好的编程习惯,无论如何都应该避免(读取数据之后,实际上会把它们从系统缓冲中删除)。窥视数据的开销是不容忽视的,因为检测可用的数据量需要一个或多个系统调用。当然,这样的开销是调用recv从系统缓冲中删除数据的开销的一部分。为了避免这样的开销,你不应该使用不断窥视系统网络缓冲中数据的方式来防止应用程序由于数据不足而完全僵住。一种方法是把应用程序分为读线程和计算线程。两个线程共享同一块数据缓冲。以一个同步对象来保护对共享缓冲的访问,比如一个event或mutex. 读线程不断的从网络上读取数据并把数据放到共享缓冲中。当读线程已经读了计算线程需要的最少数据量时,它可以把某个事件标记为有符号状态以通知计算线程开始执行。然后计算线程从缓冲中读取并删除数据并执行必要的计算。

    下面这段代码通过两个函数示例了这种方法:ReadThread负责读取网络数据,ProcessThread执行数据上的运算。
    #define MAX_BUFFER_SIZE    4096
    // Initialize critical section (data) and create 
    // an auto-reset event (hEvent) before creating the
    // two threads
    CRITICAL_SECTION data;
    HANDLE           hEvent;
    
    SOCKET           sock;
    TCHAR            buff[MAX_BUFFER_SIZE];
    int              done=0;
    
    
    // Create and connect sock
    ...
    // Reader thread
    void ReadThread(void) 
    {
        int nTotal = 0,
            nRead = 0,
            nLeft = 0,
            nBytes = 0;
    
        while (!done)   
        {
            nTotal = 0;
            nLeft = NUM_BYTES_REQUIRED;   
    		
    // However many bytes constitutes 
    // enough data for processing 
    // (i.e. non-zero)
            while (nTotal != NUM_BYTES_REQUIRED) 
            {
                EnterCriticalSection(&data);
                nRead = recv(sock, &(buff[MAX_BUFFER_SIZE - nBytes]),
                    nLeft, 0);
                if (nRead == -1)
                {
                    printf("error\n");
                    ExitThread();
                }
                nTotal += nRead;
                nLeft -= nRead;
    
                nBytes += nRead;
                LeaveCriticalSection(&data);
            }
            SetEvent(hEvent);
        }
    }
    
    // Computation thread
    void ProcessThread(void) 
    {
        WaitForSingleObject(hEvent);
    
        EnterCriticalSection(&data);
        DoSomeComputationOnData(buff);
        
        // Remove the processed data from the input
        // buffer, and shift the remaining data to
        // the start of the array
        nBytes -= NUM_BYTES_REQUIRED;
    
        LeaveCriticalSection(&data);
    }
    

            阻塞sockets的一个缺点是,同时处理多个socket通信是变得困难。依照上面的方案,我们可以修改应用程序以便每一个socket都有自己的读线程和计算线程。这种修改虽然不怎么令人愉快,但这是一种灵活的方法。唯一的不足是,一旦你开始处理大量的sockets,这种方法就不再适用了。

    Non-blocking Mode

    阻塞sockets的替代方案是非阻塞sockets。使用非阻塞的sockets会有点挑战,但它和阻塞模式一样强大,而且还有一些优点。下面的例子演示如何创建一个socket并把它设为非阻塞模式:

    SOCKET        s;
    unsigned long ul = 1;
    int           nRet;
    
    s = socket(AF_INET, SOCK_STREAM, 0);
    nRet = ioctlsocket(s, FIONBIO, (unsigned long *) &ul);
    if (nRet == SOCKET_ERROR)
    {
        // Failed to put the socket into non-blocking mode
    }
    

            一旦一个socket被设置为非阻塞模式,在其上调用的接收数据和发送数据或连接管理的Winsock API都将立即返回。在大多数情况下,这些调用返回失败(WSAEWOULDBLOCK error),意思是该请求操作在调用期间没时间完成。例如,调用recv返回WSAEWOULDBLOCK表示输入缓冲中没有数据。通常应该调用多遍直到遇到一个成功的返回值。

    Table 5-2 WSAEWOULDBLOCK Errors on Non-blocking Sockets

    Function Name

    Description

    WSAAccept and accept

    The application has not received a connection request. Call again to check for a connection.

    closesocket

    In most cases, this means that setsockopt was called with the SO_LINGER option and a nonzero timeout was set.

    WSAConnect and connect

    The connection is initiated. Call again to check for completion.

    WSARecv, recv, WSARecvFrom, and recvfrom

    No data has been received. Check again later.

    WSASend, send, WSASendTo, and sendto

    No buffer space available for outgoing data. Try again later.

            因为非阻塞调用常常返回WSAEWOULDBLOCK错误,你应该检查所有的返回值并准备处理失败。很多程序员因此陷入这样的情况:不断的调用一个函数直到它返回成功。例如,在一个紧凑循环中放一个对recv的调用来读取200字节的数据,这和之前提到的以MSG_PEEK来不断查询一个阻塞的socket的方法一样差劲。Winsock的socket I/O模型能够帮助一个应用程序判断一个socket是否准备好了读或写。

             每一个socket模式:阻塞或非阻塞,都有优点也有不足之处。阻塞模式从概念上来讲比较容易使用,但是当处理多个sockets,或以任意次数发送和接收不定数量的数据时,就变得难于管理。另一方面,非阻塞sockets比较难使用,因为需要写更多的代码可能返回的WSAEWOULDBLOCK错误。Socket I/O模型帮助应用程序以异步方式同时管理多个sockets通信。

    Socket I/O 模型

            有六种基本的socket I/O模型提供给Winsock应用程序用来管理I/O:阻塞、select、WSAAsyncSelect、WSAEventSelect、overlapped、完成端口。这部分将解释每种模型的特征并着重介绍如何使用它们来开发能够同时管理一个或多个socket请求的应用程序。

             注意,从技术角度讲,可能有一种很直接的非阻塞I/O模型,即,一个应用程序使用ioctlsocket 把所有的sockets设置为非阻塞模式。然而,情况很快就变得很糟糕,因为应用程序将会花费极大部分时间循环在这些sockets上执行I/O操作直到返回成功。

    阻塞模型

            大多数Winsock程序员一开始都是使用阻塞模型,因为它是最简单,也是最容易使用的I/O模型。使用这种模型的应用程序一般为每个socket创建1到2个线程用来处理I/O。然后每个线程简单的调用send或recv等待读或写。

             阻塞模型的最大优点是它的简单性。对于非常简单的程序和快速原型开发,这种模型很有用。它的缺点是,不适用于有多个连接的情况,因为创建多个线程将消耗宝贵的系统资源。

    select模型

            select模型是另一个在winsock中广泛使用的模型。该模型以使用select管理I/O为中心,所以我们称之为select模型。该模型起源于实现Berkeley套接字的类UNIX系统。该模型在winsock 1.1中被引入。它允许想要避免阻塞socket的程序具备有序管理多个套接字的能力。因为winsock 1.1是向后兼容Berkeley套接字实现的,所以一个使用select函数的Berkeley套接字程序,从技术上讲,应该可以毫无修改的就能运行在windows系统上。

             select函数能用来判断一个套接字上是否有数据、一个套接字是否准备好写。该函数用来防止你的应用程序阻塞在一个I/O调用上,比如在设置为阻塞模式的socket上调用send或recv,也可以用来避免非阻塞模式的socket返回WSAEWOULDBLOCK错误,这是该函数之所以存在的原因。select函数阻塞I/O操作直到参数指定的事件发生。select函数原型如下:

    int select(
        int nfds,
        fd_set FAR * readfds,
        fd_set FAR * writefds,
        fd_set FAR * exceptfds,
        const struct timeval FAR * timeout
    );
    

    第一个参数,nfds可以忽略,它的存在是为了兼容Berkeley套接字应用程序。这里有三个fd_set参数:一个用于检查可读性(readfds),一个用于检查可写性(writefds),一个用于带外数据(exceptfds)。fd_set本质上代表一个sockets集合。

    readfds指定满足如下条件的sockets:

    • 有数据可读

    • 连接被关闭、重置、或终止

    • 调用了listen函数,并且有新连接请求到来,accept函数将返回成功。

    writefds指定满足如下条件的sockets:

    • 可以发送数据

    • 正在进行的非阻塞的连接调用连接成功

    exceptfds指定满足如下条件的sockets:

    • 正在进行的非阻塞连接调用返回失败

    • 有OOB数据(带外数据)可读,传输层协议使用带外数据来发送一些重要数据

    例如,如果你想要测试一个socket的可读性时,你必须把它加到readfds集合,并且等待select函数完成。当select函数完成返回时,你必须你的socket是否仍然是readfds的一部分。如果是,你的socket就准备好读了,你可以开始从它读取数据。readfds, writefds, exceptfds中的两到三个可以为NULL值(但至少有一个不能为NULL),而且任何非空的集合中必须至少包含一个socket句柄。否则,select函数将没什么可等的。最后一个参数,timeout,是一个struct timeval指针,它决定select在I/O完成前,等待多久。如果timeout为NULL,select将无限期阻塞,直到至少有一个描述符满足指定条件。timeval结构定义如下,

    struct timeval 
    {
        long tv_sec; 
        long tv_usec;
    };
    

    tv_sec单位为秒,tv_usec单位为微秒。值为{0,0}的timeout指示select立即返回,这种方式运行应用程序轮询select操作。处于性能考虑,应该避免这样使用。当select成功执行完成,它将返回fd_set结构中处于就绪状态的sockets句柄数量。如果超时,则返回0. 出错则返回SOCKET_ERROR。

             在你开始使用select来监听sockets之前,你的应用程序必须创建一个或者所有的read, write, exception fd_set结构,并把socket句柄加入一个集合中。当你把一个socket赋值给了其中一个集合后,你就可以通过询问select,来判断所关注的I/O事件是否发生了。Winsock同了下面这几个宏用来操作和检查fd_set集合:

    • FD_ZERO(*set) 把一个集合初始化一个空集合。一个集合在使用前应该总是被清空。

    • FD_CLR(s, *set) 从集合set中删除sockt s

    • FD_ISSET(s, *set) 检查s是否是set的一个成员,是则返回TRUE

    • FD_SET(s,*set) 把socket s添加到set

            举个例子,假若你想要在不使用阻塞的情况下,弄清楚一个socket何时才可以安全的读取数据,你只需简单的把你的socket的通过使用FD_SET宏赋值给readfd集合,然后调用select函数。下述5步描述了使用select来处理一到多个sockets的应用程序的基本模型:

    1 使用FD_ZERO初始化感兴趣每一个fd_set

    2 使用FD_SET把socket句柄加入到相应的fd_set集合

    3 调用select函数并等待I/O活动触发了所提供的fd_set集合中的一到多个socket句柄。当select函数完成时,它返回所有fd_set集合中被触发的socket句柄的数量,并更新相应       的fd_set集合。

    4 根据select返回值,通过使用FD_ISSET宏检查每一个fd_set,你的应用程序可以判断哪一个socket处于I/O就绪状态。

    5 知道哪一个socket处于I/O就绪状态后,处理相应的I/O然后返回第1步继续执行select过程。(没错,要返回第一步,重新初始化fd_set集合。因为select返回后,相应的fd_set    会被更新,以指明哪写sockets处于就绪状态)。

    当select返回时,它修改每一个fd_set,删除其中没有I/O就绪操作的的socket句柄。这就是为什么你应该像第4步一样,使用FD_ISSET来判断是否一个特定的socket是否是一个fd_set集合的一部分。下面的代码概括了单socket应用程序使用select模型的基本步骤。想要添加更多的sockets到该应用程序中,只需简单的涉及维护一个列表或者一个额外的sockets数组。

    SOCKET  s;
    fd_set  fdread;
    int     ret;
    
    // Create a socket, and accept a connection
    
    // Manage I/O on the socket
    while(TRUE)
    {
        // Always clear the read set before calling 
        // select()
        FD_ZERO(&fdread);
    
        // Add socket s to the read set
        FD_SET(s, &fdread);
    
        if ((ret = select(0, &fdread, NULL, NULL, NULL)) 
            == SOCKET_ERROR) 
        {
            // Error condition
        }
    
        if (ret > 0)
        {
            // For this simple case, select() should return
            // the value 1. An application dealing with 
            // more than one socket could get a value 
            // greater than 1. At this point, your 
            // application should check to see whether the 
            // socket is part of a set.
    
            if (FD_ISSET(s, &fdread))
            {
                // A read event has occurred on socket s
            }
        }
    }
    

    使用select模型的好处是,使得单个线程可以处理多个sockets上的连接和I/O操作。这可以避免使用阻塞模型来处理多个连接时的线程膨胀情况。不足之处是,能够添加到fd_set结构中的sockets有个最大数量限制。默认情况下,这个最大值由FD_SETSIZE定义。为了加大该限制,一个应用程序可能重新定义FD_SETSIZE为更大的值。要注意这样的定义要放在包含WinSock2.h之前。虽然这个值可以改为更多的值,但如果处理大数量的sockets, select的另一个不足之处,低效,就无法避免了。幸好,我们还有更先进的模型。

    来看一个用select模型实现echo服务器的例子

    #include <sys/time.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <string.h>
    #include <arpa/inet.h>
    
    
    int open_listenfd(int port);
    
    #define  MAXBUF 1024
    
    int main(int argc, char **argv)
    {
    	int listenfd, connfd, port, n, maxfd, fd, addrlen;
    	struct sockaddr_in clientaddr;
    	fd_set readfds, readyfds;
    	char buff[MAXBUF+1];
    
    	if (argc != 2)
    	{
    		fprintf(stderr, "usage: %s <port>\n", argv[0]);
    		return 0;
    	}
    	port = atoi(argv[1]);
    	listenfd = open_listenfd(port);
    
    	FD_ZERO(&readfds);
    	FD_SET(listenfd, &readfds);
    	maxfd = listenfd;
    	addrlen = sizeof(struct sockaddr_in);
    	
    	while (1)
    	{
    		//每次使用前都要重新初始化fd_set
    		readyfds = readfds;
    		/* windows下,第一个函数可以忽略,设置为0即可 */
    		n = select(maxfd+1, &readyfds, NULL, NULL, NULL);
    		printf("n = %d\n", n);
    		if (n>0)
    		{
    			if (FD_ISSET(listenfd, &readyfds))
    			{
    				connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &addrlen);
    				printf("new connection from: %s:%d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
    				FD_SET(connfd, &readfds);
    				if (connfd > maxfd)
    					maxfd = connfd;
    			}
    			/*
    				这里windows和linux稍有不同
    			*/
    			/*********************************** windows 版本 **********************************************/
    			/* for(int i=0; i<(int)readfds.fd_count; i++)
    			 {
    				int fd = readfds.fd_array[i];
    				 if (fd != listenfd && FD_ISSET(fd, &readyfds))
    				 {
    					 n = recv(fd, buff, MAXBUF, 0);
    					 if (n<=0) //对方关闭连接
    					 {
    						 perror("recv: ");
    						 FD_CLR(fd, &readfds);
    						 close(fd);
    					 }
    					 else
    					 {
    						 buff[n] = 0;
    						 printf("fd: %d -- %s\n\n", fd, buff);
    					 }
    				 }
    			 }*/
    			 /*********************************** windows 版本 **********************************************/
    			/* 这里的循环上限是maxfd,并不是select的返回值n,这也是select效率低下的一方面:即时只有一个就绪的
    			   socket,也要遍历所有加入到fd_set集合中的sockets
    			*/
    			for (fd=0; fd<=maxfd; fd++)
    			{
    				if (fd != listenfd && FD_ISSET(fd, &readyfds))
    				{
    					n = recv(fd, buff, MAXBUF, 0);
    					if (n<=0) //对方关闭连接
    					{
    						perror("recv: ");
    						FD_CLR(fd, &readfds);
    						close(fd);
    					}
    					else
    					{
    						buff[n] = 0;
    						printf("fd: %d -- %s\n\n", fd, buff);
    					}
    				}
    			}
    		
    		}
    	}
    	return 0;
    }
    
    
    
    int open_listenfd(int port)
    {
    	struct sockaddr_in myAddr;
    	int listenfd;
    
    	/* Create a socket descriptor */
    	if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    		return -1;
    
    	/*
    	listenfd will be an end point for all requests to port on any IP address for this host
    	*/
    	bzero((char *)&myAddr, sizeof(myAddr));
    	myAddr.sin_family = AF_INET;
    	myAddr.sin_port = htons(port);
    	myAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    
    	if (bind(listenfd, (struct sockaddr *)&myAddr, sizeof(myAddr)) < 0)
    	{
    		close(listenfd);
    		return -1;
    	}
    
    	if (listen(listenfd, 1024) < 0)
    	{
    		close(listenfd);
    		return -1;
    	}
    	return listenfd;
    }


    完成端口模型

    对于新手,完成端口模型看起来很复杂,因为较之其它模型的初始化步骤,该模型需要做额外的工作以把sockets添加到完成端口。然而,你后面将会明白,一旦你理解了这些步骤,就知道它们其实并不复杂。对于必须要要同时管理多个sockets的应用程序,端口模型可以为其提供最好的系统性能。然而,该模型只有Windows NT、windows 2000、 Windows XP支持即其后的版本支持。与其它模型相比,完成端口模型提供了最好的扩展性。这个模型和好的用于处理成百上千的sockets。

    本质上,完成端口模型要求你创建一个windows完成端口对象,该对象用来管理重叠的I/O请求,并使用特定数量的线程来处理完成的重叠I/O请求。注意,一个完成端口实际上是一个windows I/O结构,该结构不只能够接受socket句柄。要开始使用完成端口模型,你需要创建一个完成端口对象用来管理任意数量的sockets句柄的多个I/O请求。这通过调用CreateIoCompletionPort函数来完成。该函数定义如下:

    HANDLE CreateIoCompletionPort(
       HANDLE FileHandle,
       HANDLE ExistingCompletionPort,
       DWORD CompletionKey,
       DWORD NumberOfConcurrentThreads
    );

    在详细讨论个参数的细节之前,要提醒注意,该函数实际上被用于两个不同的目的。

    ●  用于创建一个完成端口对象

    ●  用于把一个句柄关联到一个完成端口

    当你初始创建一个完成端口对象时,唯一需要注意的参数是NumberOfConcurrentThreads;前面三个参数可以被忽略。参数NumberOfConcurrentThreads定义了允许并发在一个完成端口上执行的线程数。理想的情况是,在但线程上,只需要一个线程来服务于完成端口,这样可以避免上下午切换。给该参数传递0值是告诉系统允许和系统中处理器数目一样多的线程并发执行。如下代码创建一个完成端口:

    CompletionPort =CreateIoCompletionPort(INVALID_HANDLE_VALUE,
    NULL, 0, 0);

    这将返回一个句柄,该句柄用来标识该完成端口。

     

    工作者线程和完成端口

    完成端口对象成功创建后,你就可以开始把sockets句柄关联到该对象。然而,在关联sockets句柄到该对象之前,你必须创建一到多个工作者线程,这些线程用来在当socket I/O请求被发送到该完成端口对象时服务该完成端口。讲到这里,你可能会问,应该创建多少个线程来服务该完成端口呢?实际上,这是完成端口模型复杂性体现之一,因为需要用来服务I/O请求的线程数量取决于你的应用程序的总体设计。当调用CreateIoCompletionPort时指定的并发线程数和创建的工作者线程数是相互区别的,注意到这一点很重要。它们表示的不是同样的东西。之前我们推荐你应该通过CreateIoCompletionPort为每个处理器指定一个线程以避免上下文切换。CreateIoCompletionPort函数的NumberOfConcurrentThreads参数显示告诉系统只允许n个线程同时操作该完成端口。如果你在该完成端口上创建了多于n的工作者线程,也只允许同时有n个线程操作该完成端口。(实际上,系统可能在一小段时间会超过这个值,不过系统会快速的讲到你在CreateIoCompletionPort指定的值)你可能会奇怪,会什么你会创建多于调用CreateIoCompletionPort指定的线程数呢?正如我们前面提到的,这取决于你的应用程序的总体设计。假如你的其中之一个线程调用了一个函数 --- 比如Sleep或者WaitForSingleObject --- 并且被挂起了,另一个线程就可以使用它所占用的资源(比如完成端口)。换句话说,你总是希望有和你调用CreateIoCompletionPort指定允许的线程数一样多的线程可用于执行。因此,如果你预期你的工作者线程可能会阻塞,那么,创建数量多于CreateIoCompletionPort参数NumberOfConcurrentThreads指定的线程数的工作者线程就是合理的。

             一旦你有了足够多的工作者线程来服务完成端口上的I/O请求,你就可以开始把socket句柄关联到该完成端口。这要求你在一个已创建的完成端口上调用CreateIoCompletionPort函数并提供前三个参数 --- FileHandle, ExistingCompletionPort, and Completion Key – 这三个参数提供了与之相关的socket信息。FileHandle参数代表一个与完成端口相关联的socket句柄。ExistingCompletionPort指明socket被关联到的完成端口。CompletionKey参数指明每次出来某个特定的socket句柄是可以访问的数据。应用程序可以通过该参数指定与socket相关的任何数据类型。我们叫它句柄单元数据(per-handle data),因为它代表一个与句柄相关联的数据。这是个很有用的参数,你可以把这个参数当做一个指向一个数据结构的指针,而该数据结构包含socket句柄和其它socket相关信息。在本章的后面,我们将会看到,服务于完成端口的线程实例可以通过这个参数来获取与socket相关的信息。

             让我们根据上述描述来开始构建一个基本的应用程序框架。下面这个例子示例了如何使用一个完成端口模型开始开发一个echo服务器。在代码中,我们遵循一下步骤:

    1.        创建一个完成端口。第四个参数设为0,指定同一时刻只允许每个处理器一个线程线程运行在完成端口行。

    2.        检查系统上存在多少个处理器。

    3.        基于第2步获得的处理器信息,创建工作者线程来处理完成端口上已经完成的I/O请求。在这个简单的例子中,我们为每个处理器创建一个线程,因为我们预期我们的线程不会挂起以至没有足够的线程分配给每个处理器执行。调用CreateThread函数时,你必须提供一个线程例程,这个线程例程是线程开始执行的地方。我们将在这部分的讨论工作者线程的职责。

    4.        准备一个监听套接字来监听端口5150上的连接。

    5.        使用accept函数接受到来的连接

    6.        创建一个表示句柄单元数据的数据结构,并且把已接受的连接的socket句柄存储在该数据结构中。

    7.        通过调用CreateIoCompletionPort函数,把accept函数返回的新的socket句柄关联到完成端口。通过completion key parameter参数把句柄单元数据传给CreateIoCompletionPort。

    8.        开始处理已接受连接上的I/O。


    I/O模型的选择

    当设计应用程序时怎选择I/O模型呢?每一个模型都有它的优点和不足之处。与开发一个简单,有多个服务线程的阻塞模型程序相比,所有的I/O模型都要进行相当复杂的编码。

    客户端开发

    当你开发一个管理一到多个sockets的客户端应用程序时,我们推荐你使用重叠I/O模型或者出于性能考虑优先使用WSAEventSelect模型。但是,如果你正在开发基于windows的应用程序,WSAAsyncSelect可能是更好的选择,因为用WSAAsyncSelect本身的消息处理模型,你的应用程序就具备了处理消息的能力。

    服务端开发

    如果你正在开发一个要处理多个sockets的服务器,处于性能方面考虑,我们推荐你优先使用重叠事件I/O。但是,如果你的服务器要服务于大量的I/O请求,你应该考虑使用完成端口模型,这回获得更好的系统性能。


    翻译自《windows网络编程》第二版,Winsock I/O Methods一章

    展开全文
  • 通过分析"六大系统"的构成,给出了"六大系统"评价指标体系,构造了基于未确知测度理论的单指标测度函数评价模型,并通过信息熵确立指标的分类权重。结果表明,采用该方法得到的评价结果能够准确、客观、全面地反映安全...
  • 关系模型 关系数据库的标准语言 关系数据库的逻辑设计 针对一个具体的问题,应该如何构造一个适合于她的数据模式,即应该构造几个关系,每个关系由哪些属性组成等。 问题–什么是一个好的数据库逻辑设计 关系数据库...

    关注公众号凡花花的小窝,收获更多的考研计算机专业编程相关的资料
    6.1问题的提出
    关系数据库的基本概念
    关系模型
    关系数据库的标准语言
    关系数据库的逻辑设计
    针对一个具体的问题,应该如何构造一个适合于她的数据模式,即应该构造几个关系,每个关系由哪些属性组成等。
    问题–什么是一个好的数据库逻辑设计
    关系数据库逻辑设计
    针对具体问题,如何构造一个适合于它的数据模式
    数据库逻辑设计的工具──关系数据库的规范化理论
    一、概念回顾
    二、关系模式的形式化定义
    三、什么是数据依赖
    四、关系模式的简化定义
    五、数据依赖对关系模式影响

    一、概念回顾
    关系

    关系模式

    关系数据库

    关系数据库的模式
    一个例子
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    什么是一个好的模式
    好的模式不会发生插入异常,删除异常,更新异常,数据冗余应该尽量的减少
    由于模式中的某些数据依赖引起的
    用规范化的理论改造关系模式,消除其中的不合适的数据依赖。
    二、关系模式的形式化定义
    关系模式由五部分组成,即它是一个五元组:
    R(U, D, DOM, F)
    R: 关系名
    U: 组成该关系的属性名集合
    D: 属性组U中属性所来自的域
    DOM: 属性向域的映象集合
    F: 属性间数据的依赖关系集合

    三、什么是数据依赖在这里插入图片描述

    1. 完整性约束的表现形式
      限定属性取值范围:例如学生成绩必须在0-100之间
      定义属性值间的相互关连(主要体现于值的相等与否),
      即通过属性间值的相等与否来描述
      这就是数据依赖,它是数据库模式设计的关键

    2.数据依赖
    是通过一个关系中的属性间的值相等与否来体现出来的数据的相互关系
    是现实世界属性间相互联系的抽象
    是数据内在的性质
    是语义的体现
    类型
    函数依赖Functional Dependecy简单记为FD
    多值依赖Mutivalued Dependecy简记为NVD
    连接依赖
    。。。。
    数据依赖对关系模式的映像
    不合适的数据依赖,造成的插入异常,删除异常,更新异常,和数据冗余问题
    3.关系模式的简化表示
    关系模式R(U, D, DOM, F)
    简化为一个三元组:
    R(U, F)
    当且仅当U上的一个关系r满足F时,r称为关系模式 R(U, F)的一个关系
    当且仅当U上的一个关系r满足F的时候,r称为关系模式R(U,F)的一个关系。
    在这里插入图片描述
    [例1]建立一个描述学校教务的数据库:
    学生的学号(Sno)、所在系(Sdept)
    系主任姓名(Mname)、课程名(Cname)
    成绩(Grade)

    单一的关系模式 : Student <U、F>
    U ={ Sno, Sdept, Mname, Cname, Grade }
    属性组U上的一组函数依赖F:
    F ={ Sno → Sdept, Sdept → Mname,
    (Sno, Cname) → Grade }
    在这里插入图片描述
    关系模式Student<U, F>中存在的问题

    1. 数据冗余太大
    2. 更新异常(Update Anomalies)
    3. 插入异常(Insertion Anomalies)
    4. 删除异常(Deletion Anomalies)

    结论:
    Student关系模式不是一个好的模式。
    “好”的模式:
    不会发生插入异常、删除异常、更新异常,
    数据冗余应尽可能少
    原因:由存在于模式中的某些数据依赖引起的
    解决方法:通过分解关系模式来消除其中不合适
    的数据依赖
    把这个单一模式分成3个关系模式:
    S(Sno,Sdept,Sno → Sdept);
    SC(Sno,Cno,Grade,(Sno,Cno) → Grade);
    DEPT(Sdept,Mname,Sdept→ Mname)

    第3讲函数依赖以及范式的概念
    6.2 规范化
    规范化理论正是用来改造关系模式,通过分解关系模式来消除其中不合适的数据依赖,以解决插入异常、删除异常、更新异常和数据冗余问题。
    6.2.1 函数依赖
    函数依赖
    平凡函数依赖与非平凡函数依赖
    完全函数依赖与部分函数依赖
    传递函数依赖
    一、函数依赖
    定义6.1 设R(U)是一个属性集U上的关系模式,X和Y是U的子集。
    若对于R(U)的任意一个可能的关系r,r中不可能存在两个元组在X上的属性值相等, 而在Y上的属性值不等, 则称 “X函数确定Y” 或 “Y函数依赖于X”,记作X→Y。 在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    函数依赖不是指关系模式R的某个或者某些关系实例r满足约束条件,而是指R的所有关系实例r均要满足的约束条件。
    说明
    1. 所有关系实例均要满足

    1. 语义范畴的概念

    2. 数据库设计者可以对现实世界作强制的规定
      如何确定函数依赖
      函数依赖是语义范畴的概念,只能根据数据的语义来确定函数依赖
      比如Sname->Sno的函数依赖只有在学生不允许有重名的条件下成立。
      数据库设计者可以对现实世界作强制的规定
      例如设计者可以强行规定不允许学生有崇明,因而使函数依赖,Sname->Sno,Sname->Ssex,Sname->Sdept成立
      函数依赖是指关系模式R在任何时刻的关系实例都要满足的约束条件
      不是指某个或者某些关系实例r满足的约束条件,而是指实例r均要满足的约束条件。
      2.平凡函数依赖与非平凡函数依赖
      在关系模式R(U)中,对于U的子集X和Y,
      如果X→Y,但Y  X,则称X→Y是非平凡的函数依赖
      若X→Y,但Y  X, 则称X→Y是平凡的函数依赖
      例:在关系SC(Sno, Cno, Grade)中,
      非平凡函数依赖: (Sno, Cno) → Grade
      平凡函数依赖: (Sno, Cno) → Sno
      (Sno, Cno) → Cno
      在这里插入图片描述
      在这里插入图片描述
      若X→Y,则X称为这个函数依赖的决定属性组,也称为决定因素(Determinant)。
      若X→Y,Y→X,则记作X←→Y。
      若Y不函数依赖于X,则记作X→Y。

    在这里插入图片描述
    三、完全函数依赖与部分函数依赖
    定义6.2 在R(U)中,如果X→Y,并且对于X的任何一个真子集X’,都有X’ Y, 则称Y对X完全函数依赖,记作
    X F Y。
    若X→Y,但Y不完全函数依赖于X,则称Y对X部分函数依赖,记作X P Y。

    4.传递函数依赖
    在这里插入图片描述

    [例1] 中(Sno,Cno)→Grade是完全函数依赖,
    (Sno,Cno)→Sdept是部分函数依赖
    因为Sno →Sdept成立,且Sno是(Sno,Cno)的真子集

    四、传递函数依赖
    定义6.3 在R(U)中,如果X→Y,(Y X) ,Y→X Y→Z, 则称Z对X传递函数依赖。
    记为:X → Z

    注: 如果Y→X, 即X←→Y,则Z直接依赖于X。

    例: 在关系Std(Sno, Sdept, Mname)中,有:
    Sno → Sdept,Sdept → Mname
    Mname传递函数依赖于Sno

    在这里插入图片描述
    在这里插入图片描述
    6.2.2 码
    定义6.4 设K为R<U,F>中的属性或属性组合。若K U, 则K称为R的侯选码(Candidate Key)。
    若候选码多于一个,则选定其中的一个做为主码(Primary Key)。
    在这里插入图片描述
    主属性与非主属性
    包含在任何一个候选码中的属性 ,称为主属性(Prime attribute)
    不包含在任何码中的属性称为非主属性(Nonprime attribute)或非码属性(Non-key attribute)
    全码
    整个属性组是码,称为全码(All-key)
    全码整个属性组是码,称为全码,
    不包含在任何码中的属性称为非主属性,或称为非码属性
    [例2]
    关系模式S(Sno,Sdept,Sage),单个属性Sno是码,
    SC(Sno,Cno,Grade)中,(Sno,Cno)是码
    [例3]
    关系模式R(P,W,A)
    P:演奏者 W:作品 A:听众
    一个演奏者可以演奏多个作品
    某一作品可被多个演奏者演奏
    听众可以欣赏不同演奏者的不同作品
    码为(P,W,A),即All-Key
    在这里插入图片描述
    在这里插入图片描述
    包含在任何一个候选码中的属性,称为主属性
    不包含在任何码中的属性称为非主属性
    全码:整个属性组是码
    定义6.5 关系模式 R 中属性或属性组X 并非 R的码,但 X 是另一个关系模式的码,则称 X 是R 的外部码(Foreign key)也称外码
    如在SC(Sno,Cno,Grade)中,Sno不是码,但Sno是关系模式S(Sno,Sdept,Sage)的码,则Sno是关系模式SC的外部码
    主码与外部码一起提供了表示关系间联系的手段
    主码与外部吗一起提供了表示关系间联系的手段

    展开全文
  • 了解select模型六大注意点

    千次阅读 2015-03-07 10:42:11
    Select模型出现的目的::模型的出现是为了解决“一个客户端一线程”的问题,为了WINDOWS的线程切换不要太频繁。   select函数 int select( int maxfdp, //忽略 fd_set *readfds,//检查可读性 fd_set *writefds, ...

    Select模型出现的目的::模型的出现是为了解决“一个客户端一线程”的问题,为了WINDOWS的线程切换不要太频繁。

     

    select函数

    int select(

    int maxfdp,   //忽略

    fd_set *readfds,//检查可读性

    fd_set *writefds,      //检查可写性

    fd_set *errorfds,      //检查错误

    struct timeval *timeout   //指定函数等待的最长时间

    );

     

    1、理解struct fd_set(一个套接字集合)

    struct fd_set可以理解为一个集合,这个集合中存放的是文件描述符(filedescriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。

     

    2、使用struct fd_set

    FD_ZERO(fd_set * set); //清空集合

    FD_SET(s,fd_set * set); //添加套接字集合

    FD_CLR(s ,fd_set* set); //从set移除套接字s

    FD_ISSET(s ,fd_set* set);//检查集合中s是否set的成员

     

     

    3、struct timeval

    struct timeval {   

    time_t tv_sec; /* 秒*/   

    suseconds_t tv_usec; /* 微秒*/  };

     

    4、参数含义

    fd_set*readfds我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。   

    fd_set*writefds我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。   

    fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。

     

    5、超时的设置

    struct timeval *timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态

    ①、若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;

    ②、若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;

    ③、timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

     

    6、返回值:   

    负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件


     
    展开全文
  • 一、基矩阵 + 非基矩阵 约束条件、 二、基矩阵 + 非基矩阵 线性规划、 三、线性规划 可行解、 四、目标函数 推导、 五、目标函数最大 分析、 、总结



    在上一篇博客 【运筹学】线性规划数学模型 ( 单纯形法原理 | 单纯形法流程 | 查找初始基可行解 ) 中 , 讲解到了使用单纯形法求解线性规划问题 , 需要解决以下三个主要问题 :

    • 查找初始基可行解
    • 判定是否是最优解
    • 如何迭代

    该博客中已经讲解了如何查找初始基可行解 , 查找初始基可行解时 , 优先选择单位阵作为基矩阵 , 单位阵 II 对应的基解 , 必定是基可行解 ;
    ( 如果没有单位阵 II , 那么后续在讨论 )


    本博客开始讲解 , 如何 判定最优解 ( 最优解是如何确定出来的 ) , 和 如何迭代到下一个基可行解 ;





    一、基矩阵 + 非基矩阵 约束条件



    目标函数 , 用于判定 11 个基可行解是否是最优解 ;


    【运筹学】线性规划数学模型 ( 求解基矩阵示例 | 矩阵的可逆性 | 线性规划表示为 基矩阵 基向量 非基矩阵 非基向量 形式 ) 博客中 , 根据推导 , 线性规划的约束条件 , 可以表示为 :

    BXB+NXN=bBX_B + NX_N = b





    二、基矩阵 + 非基矩阵 线性规划



    将上述约束条件代入线性规划标准形式中

    maxZ=j=1ncjxj{j=1naijxj=bi(i=1,2m)xj0(i=1,2n)\begin{array}{lcl}max Z = \sum_{j=1}^{n}c_j x_j\\ \\ \begin{cases} \sum_{j=1}^{n} a_{ij}x_j = b_i & (i = 1 , 2 \cdots m) \\ \\x_j \geq 0 & (i = 1 , 2 \cdots n) \end{cases}\end{array}


    得到如下形式 :

    maxZ=CBTXB+CNTXN{BXB+NXN=bxj0(i=1,2n)\begin{array}{lcl}max Z = C_B^TX_B + C_N^TX_N \\ \\ \begin{cases} BX_B + NX_N = b \\ \\x_j \geq 0 & (i = 1 , 2 \cdots n) \end{cases}\end{array}


    假设得到基解 {XB=B1bXN=O\begin{cases} X_B = B^{-1}b \\ \\X_N = O \end{cases} , 其中 OO 表示零矩阵 , 矩阵张红每个元素的值都是 00 ;


    判断该基解 (XBXN)\begin{pmatrix} X_B \\ X_N \\ \end{pmatrix} 是否是最优解 , 需要从目标函数 maxZ=CBTXB+CNTXNmax Z = C_B^TX_B + C_N^TX_N 开始分析 ;





    三、线性规划 可行解



    从现在开始不再讨论基解了 , 回到之前 , 讨论可行解 , XNX_N 可以取值任意合法值 , 而不是取 OO 矩阵值 , 查看取值其它值的时候 , 目标函数是否有最大值 , 这里 重新进行解的推导 :

    【运筹学】线性规划数学模型 ( 线性规划求解 | 根据非基变量的解得到基变量解 | 基解 | 基可行解 | 可行基 ) 二、根据非基变量的解得到可行解 博客章节 , 在 BXB+NXN=bBX_B + NX_N = b 两端都乘以 B1B^{-1} , 然后移项得到了 :

    XB=B1bB1NXNX_B = B^{-1}b - B^{-1}NX_N

    将上述可行解 , 列举出来 :

    {XB=B1bB1NXNXN\begin{cases} X_B = B^{-1}b - B^{-1}NX_N \\ \\X_N \end{cases}





    四、目标函数 推导



    此时进行判定线性规划可行解 {XB=B1bB1NXNXN\begin{cases} X_B = B^{-1}b - B^{-1}NX_N \\ \\X_N \end{cases} 中 , XNX_N 取值 OO 矩阵 , 是否是最好的情况 , 即目标函数达到最大值 , 目标函数如下 :

    maxZ=CBTXB+CNTXNmax Z = C_B^TX_B + C_N^TX_N


    XB=B1bB1NXNX_B = B^{-1}b - B^{-1}NX_N 代入上述目标函数 :

    maxZ=CBT(B1bB1NXN)+CNTXN=CBTB1bCBTB1NXN+CNTXN\begin{array}{lcl} max Z &=& C_B^T ( B^{-1}b - B^{-1}NX_N ) + C_N^TX_N \\\\ &=& C_B^T B^{-1}b - C_B^T B^{-1}NX_N + C_N^TX_N \end{array}


    CBTB1bC_B^T B^{-1}b 计算结果是一个数值常量 , 可以写成 b0b_0 , 与 XX ( nn 个决策变量 ) 无关 ;


    =b0+(CNTCBTB1N)XN\begin{array}{lcl} &=& b_0 + ( C_N^T - C_B^T B^{-1}N )X_N \\\\ \end{array}


    之前的基解的策略是 , 将 XNX_N 取值为 OO 零矩阵 , 现在讨论 , 要使上述目标函数 maxZmaxZ 最大 , 分析 XN=OX_N = O 是否是最好的选择 , 即分析 XN=OX_N = O 是否是使 maxZmaxZ 目标函数最大的值 ;


    假设 XNX_N 矩阵中的变量值为 (xm+1xm+2xn)\begin{pmatrix} x_{m+1} \\ x_{m+2} \\ \vdots\\ x_n \end{pmatrix} , (CNTCBTB1N)( C_N^T - C_B^T B^{-1}N ) 的计算结果是 (σm+1,σm+2,,σn)\begin{pmatrix} \sigma_{m+1} , \sigma_{m+2} , \cdots , \sigma_n \end{pmatrix} , (CNTCBTB1N)XN( C_N^T - C_B^T B^{-1}N )X_N 结果是 σm+1xm+1+σm+2xm+2++σnxn\sigma_{m+1} x_{m+1} + \sigma_{m+2} x_{m+2} + \cdots + \sigma_{n} x_{n}


    =b0+(CNTCBTB1N)XN=b0+σm+1xm+1+σm+2xm+2++σnxn\begin{array}{lcl} &=& b_0 + ( C_N^T - C_B^T B^{-1}N )X_N \\\\ &=& b_0 + \sigma_{m+1} x_{m+1} + \sigma_{m+2} x_{m+2} + \cdots + \sigma_{n} x_{n} \\\\ \end{array}





    五、XN=OX_N = O 目标函数最大 分析



    当上述 XNX_N 矩阵中的变量值 (xm+1xm+2xn)\begin{pmatrix} x_{m+1} \\ x_{m+2} \\ \vdots\\ x_n \end{pmatrix} 都为 00 时 , 假如上述公式取值最大值 , 即

    b0+σm+1xm+1+σm+2xm+2++σnxnb_0 + \sigma_{m+1} x_{m+1} + \sigma_{m+2} x_{m+2} + \cdots + \sigma_{n} x_{n}

    取值最大值 ;


    在线性规划约束条件中 , 所有的变量都是大于等于 00 , 每个 xjx_j 约束变量取值都可以大于等于 00 , 目前是查看当所有的 xjx_j 变量都取值 00 时 , 目标函数达到最大值的情况 ;


    XNX_N 取值等于 OO 零矩阵时 , 目标函数值等于 b0b_0 , XNX_N 中有元素取值大于 00 时 , 就会在 b0b_0 基础上加上一个值 , 如果这个值是 小于等于 00 的 , 那么对应的 xjx_j 取值越大 , 目标函数值越小 ;


    因此这里得到 , 在 XN=(xm+1xm+2xn)X_N=\begin{pmatrix} x_{m+1} \\ x_{m+2} \\ \vdots\\ x_n \end{pmatrix} 非基变量前的系数是小于等于 00 , 才能满足XNX_N 中的元素取值等于 00 , 目标函数是最大值 ;


    因此
    b0+σm+1xm+1+σm+2xm+2++σnxnb_0 + \sigma_{m+1} x_{m+1} + \sigma_{m+2} x_{m+2} + \cdots + \sigma_{n} x_{n}

    中的 σm+1,σm+2,,σn\sigma_{m+1} , \sigma_{m+2} , \cdots , \sigma_{n} 系数值小于等于 00 , 其中每个系数对应的变量 xjx_{j} 必定是大于等于 00 的值 , 那么系数 σm+1\sigma_{m+1} 小于等于 00 时 , 每个变量取值 xj=0x_j = 0 , 目标函数达到最小值 ;





    六、总结



    将线性规划约束条件表示为 BXB+NXN=bBX_B + NX_N = b


    进行变换后得到 XB=B1bB1NXNX_B = B^{-1}b - B^{-1}NX_N


    这里可以写出如下可行解 {XB=B1bB1NXNXN\begin{cases} X_B = B^{-1}b - B^{-1}NX_N \\ \\X_N \end{cases}


    将上述可行解代入目标函数 maxZ=CBTXB+CNTXNmax Z = C_B^TX_B + C_N^TX_N


    得到 maxZ=b0+(CNTCBTB1N)XNmaxZ = b_0 + ( C_N^T - C_B^T B^{-1}N )X_N


    在该情况下 , 如果 (CNTCBTB1N)( C_N^T - C_B^T B^{-1}N ) 系数小于等于 00 , 当 XNX_N 取值为 00 时 , 目标函数得到最大值 ;

    展开全文
  • 高斯混合模型(GMM)指的是多个高斯分布函数的线性组合,理论上GMM可以拟合出任意类型的分布,通常用于解决同一集合下的数据包含多个不同的分布的情况。 灵魂的拷问:为什么GMM可以拟合出任意类型的分布? AI...
  • 步法实现模型搭建 导入相关模块 导入训练集与测试集 搭建网络结构 配置训练方法 执行训练方法 打印出网络结构,参数统计 基本元素介绍 一、激活函数 激活函数是用来加入非线性因素的,因为线性模型的表达能力...
  • 一、内存分区模型 //内存方向分为四个区域 //代码区:存放函数体的二进制代码,由操作系统进行管理 //全局区:存放全局变量和静态变量以及常量 //栈区:由编译器自动分配释放,存放函数的参数值,局部变量等 //堆...
  • 模型的训练主要有内置fit方法、内置tran_on_batch方法、自定义训练循环。 注:fit_generator方法在tf.keras中不推荐使用,其功能已经被fit包含。 一、内置fit方法 该方法功能非常强大, 支持对numpy array, tf.data....
  • PyML()——用sklearn训练KNN模型

    千次阅读 2018-07-25 17:07:21
    机器学习算法可以被分为两类:参数模型和变参模型。 对于参数模型,在训练过程中我们要学习一个函数,重点是估计函数的参数,然后对于新数据集,我们直接用学习到的函数对 齐分类。典型的参数模型包括感知机、...
  • 尽管最合适的模型差异很,但它们的指数对却表现出显着的格局,因为它们的差接近1/3(例如:von Bertalanffy指数对)。 这就定义了一类新的模型,为此,本文提供了将增长与食物消费相关联的生物学动机。
  • 章 逻辑斯蒂回归与最大熵模型 logistic分类器是由一组权值系数组成的,最关键的问题就 是如何获取这组权值,通过极似然函数估计获得,并且 Y~f(x;w) 似然函数是统计模型中参数的函数。给定输出x时,关于参 数...
  • 因为事件Y=1发生的几率是x的线性函数,所以x越事件发生的几率越。当x趋近于正无穷,事件发生的几率是1.负无穷,事件发生的几率是0 参数估计 利用极似然法: K项逻辑斯蒂回归模型 最大熵模型 首先用一个例子...
  • 而通过极似然法来推导模型的参数估计问题 通过对模型参数的似然函数通过求导来得到递归方程 通过公式可以看出logist是对前面的感知机的升级版,感知机的判断方式过于简单。而其梯度下降的时候也将sign的去掉了,...
  • Spark编程模型与Spark基本架构 在程序上创建Spark Context,在上下文定义一系列的数据处理函数,加载数据的处理方式。在客户端有个Driver Application,调度编译程序执行,包括ActorSystem、Block Manager、...
  • 1 GMM基础高斯混合模型(GMM)指的是多个高斯分布函数的线性组合,理论上GMM可以拟合出任意类型的分布,通常用于解决同一集合下的数据包含多个不同的分布的情况。灵魂的拷问:为什么GMM可以拟合出任意类型的分布?AI...
  • (一)神经网络简介 主要是利用计算机的计算能力,对大量的样本进行拟合,最终得到一个我们想要的结果,结果通过0-1编码,这样就OK啦 (二)人工神经网络模型 一、基本单元的三个基本要素 1、一组连接(输入),上面...
  • 章:I/O复用:select和poll函数

    千次阅读 2011-10-30 23:57:49
    章I/O复用:select和poll函数 1 unix提供给我们的五种IO模型: 阻塞I/O模型:阻塞I/O例如函数recvfrom的内核到应用层、应用层到内核的调用过程是这样的:首先把描述符、接受数据缓冲地址、大小传递给内核,...
  • 逻辑斯蒂回归模型 logistic regression逻辑斯蒂分布函数 logistic distribution 对应下面两张示意图摘自维基百科逻辑斯蒂密度函数二项回归模型:将x对应的样本归于概率值较的分类...
  • 56.1 UDP 编程模型 56.1.1 编程模型 ...函数参数: sockfs:套接字文件描述符 buf:发送的数据 len:发送的数据的大小,即多少个字节 flags:一般设置为0 dest_addr:接收方的地址 addrlen:...
  • 春节前后时间概花了不到一个月在研究BS方程,因为工作中要用到股指期货delta对冲,对于方程怎么推导怎么求解大致明白了。 曾经这个公式让两个人获得诺贝尔经济学奖,也曾经让一家非常豪华的对冲基金破产。 长期...
  • 实际我们应用的SVM模型都是核函数+软间隔的支持向量机,那么,有以下原因导致SVM过拟合: 选择的核函数过于powerful,比如多项式核中的Q设置的次数过高 要求的间隔过,即在软间隔支持向量机中C的参数过时,...
  • 高考数学题六大解题思路 很多高三学生解题的时候没有按照思路,导致自己数学成绩不高。以下是整理的高考数学解题思路,希望可以分享高三的学生进行参考和运用。 1.函数与方程思想 函数思想是指运用运动变化的观点...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 460
精华内容 184
关键字:

六大函数模型