2013-08-04 45 views
0

現狀多線程編程:在變量'iget'等於變量'iput'的情況下?

閱讀Unix套接字編程後,W.Richard史蒂芬,我正在寫一個P2P程序在主線程創建在五個子線程住線程池。然後它用kqueue()監視50個插座。當一個事件發生在一個指定的套接字中(例如,在套接字上接收數據)時,主線程將套接字描述符複製到一個共享數組中並喚醒線程池中的一個線程。子線程然後處理來自套接字的請求。另外,我使用互斥變量和條件變量來保護共享數組。

問題

筆者提出一種在書中第30.12及30.13的源代碼「服務器/ serv08.c」和「服務器/ pthread08.c」,分別作爲如果沒有的東西這段代碼錯了。但是,當我編寫類似於一位作者的代碼片段時,線程同步效果不佳。爲什麼iput在主線程中等於iget

代碼
--global變量 -

typedef struct tagThread_information 
{ 

    int  sockfd; 

} Thread_information; 
Thread_information  peer_fds[MAX_THREAD]; 
pthread_mutex_t   peerfd_mutex; 
pthread_cond_t   peerfd_cond; 
pthread_mutex_t   STDOUT_mutex; 
int      iput; 
int      iget; 

- 主thread--

void Wait_for_Handshake(download_session *pSession, int nMaxPeers) 
{ 
struct kevent ev[50], result[50]; 
int    kq, i, nfd; 
int c = 1; 

if((kq = kqueue()) == -1) 
{ 
    fprintf(stderr, "fail to initialize kqueue.\n"); 
    exit(0); 
} 

for(i = 0 ; i < nMaxPeers; i++) 
{ 
    EV_SET(&ev[i], pSession->Peers[i].sockfd, EVFILT_READ, EV_ADD, 0, 0, 0); 
    printf("socket : %d\n", (int)ev[i].ident); 
} 

// create thread pool. initialize mutex and conditional variable. 
iput = 0; 
iget = 0; 
pthread_mutex_init(&STDOUT_mutex, NULL); 
pthread_mutex_init(&peerfd_mutex, NULL); 
pthread_cond_init(&peerfd_cond, NULL); 

// Assume that MAX_THREAD is set to 5. 
for(i = 0 ; i < MAX_THREAD; i++) 
    thread_make(i); 


while(1)  
{   
    nfd = kevent(kq, ev, nMaxPeers, result, nMaxPeers, NULL); 

    if(nfd == -1) 
    { 
     fprintf(stderr, "fail to monitor kqueue. error : %d\n", errno); 
     nMaxPeers   = Update_peer(ev, pSession->nPeers); 
     pSession->nPeers = nMaxPeers; 
     continue; 
    } 

    for(i = 0 ; i < nfd; i++) 
    { 

     pthread_mutex_lock(&peerfd_mutex); 
     peer_fds[iput].sockfd = (int)result[i].ident; 
     if(++iput == MAX_THREAD) 
      iput = 0; 
     if(iput == iget) // Here is my question. 
     { 
      exit(0); 
     } 

     pthread_cond_signal(&peerfd_cond); 
     pthread_mutex_unlock(&peerfd_mutex); 


    } 

} 

} 

--sub thread--

void * thread_main(void *arg) 
{ 
    int  connfd, nbytes; 
    char buf[2048]; 
    for(; ;) 
    { 
     /* get socket descriptor */ 
     pthread_mutex_lock(&peerfd_mutex); 
     while(iget == iput) 
      pthread_cond_wait(&peerfd_cond, &peerfd_mutex); 
     connfd = peer_fds[iget].sockfd; 
     if (++iget == MAX_THREAD) 
      iget = 0; 
     pthread_mutex_unlock(&peerfd_mutex); 


    /* process a request on socket descriptor. */ 
    nbytes = (int)read(connfd, buf, 2048); 

    if(nbytes == 0) 
    { 
     pthread_mutex_lock(&STDOUT_mutex); 
     printf("\n\nthread %ld, socket : %d, nbytes : %d\n\n\n", (long int)pthread_self(), connfd, nbytes); 
     printf("socket closed\n\n"); 
     pthread_mutex_unlock(&STDOUT_mutex); 
     close(connfd); 
     continue; 
    } 
    else if(nbytes == -1) 
    { 
     close(connfd); 
     pthread_mutex_lock(&STDOUT_mutex); 
     printf("\n\nthread %ld, socket : %d, nbytes : %d\n\n\n", (long int)pthread_self(), connfd, nbytes); 
     perror("socket error : "); 
     write(STDOUT_FILENO, buf, nbytes); 
     printf("\n\n\n\n"); 
     pthread_mutex_unlock(&STDOUT_mutex); 

     continue; 
    } 

    pthread_mutex_lock(&STDOUT_mutex); 
    printf("\n\nthread %ld, socket : %d, nbytes : %d\n\n\n", (long int)pthread_self(), connfd, nbytes); 
    write(STDOUT_FILENO, buf, nbytes); 
    printf("\n\n\n\n"); 
    pthread_mutex_unlock(&STDOUT_mutex); 

} 
} 

回答

0

在你主線程:

if(++iput == MAX_THREAD) 
      iput = 0;// so iput is 0 --> MAX_THREAD 

而在你的子線程:

if (++iget == MAX_THREAD) 
      iget = 0;// So iget is 0 --> MAX_THREAD 

由於子線程和主線程運行在「同時」,它們是值如果看不到.the iput也許equare到的iget有時。

+0

是的,子線程可以在'iget'等於'iput'時執行。但是當'iput' =='iget'時,由於互斥操作,主線程進入睡眠狀態,對嗎?但我的主線程通過'if(iget == iput)'死掉。但作者從來沒有解釋過在主線程中通過if語句。所以我的問題是'iput'在主線程中等於'iget'嗎? – inherithandle

+0

@inherithandle當iput == iget時,子線程進入睡眠狀態,等待條件變量。不是主線程。 –

0

從「UNIX網絡Prgramming第1卷,第2版」,章節27.12,757頁,從註釋到行server/serv08.c 27-38:

我們還檢查iput指數也抓不到與iget索引相關,這表明我們的陣列不夠大。

作爲參考上述(take from here)中提到的線:

27 for (; ;) { 
28 clilen = addrlen; 
29 connfd = Accept(listenfd, cliaddr, &clilen); 
30 Pthread_mutex_lock(&clifd_mutex); 
31 clifd[iput] = connfd; 
32 if (++iput == MAXNCLI) 
33  iput = 0; 
34 if (iput == iget) 
35  err_quit("iput = iget = %d", iput); 
36 Pthread_cond_signal(&clifd_cond); 
37 Pthread_mutex_unlock(&clifd_mutex); 
38 } 
0

你有什麼有一個典型的循環緩衝器實現。

頭指針和尾指針/索引指向相同的位置時,環形緩衝器是空的。你可以在代碼while (iget == iput) ...中看到這個測試,這意味着「隊列是空的......」。

如果在循環緩衝區的頭部插入後,頭指向尾部,那是一個問題。緩衝區溢出。 這是一個問題,因爲現在緩衝區現在看起來是空的,即使它已滿。

也就是說,一個未使用的位置被保留在緩衝區中;如果緩衝區有4096個條目,我們只能填充4095.如果我們填充4096,那麼我們有溢出:它看起來像一個空的循環緩衝區。我們可以使用全部4096個位置,如果我們允許索引從0到8192,使用一個額外的位來解決歧義,所以,而不是在4095之前包裝到零,指針將保持到4096。 .. 8191.當然,爲了恢復一個浪費的元素,我們必須記得訪問陣列模4096.)

它看起來像循環緩衝區上的代碼保存溢出,因爲它的結構使得這種情況不會發生,所以它構成了一個內部錯誤。當循環過多的描述符從生產者傳遞給消費者時,循環緩衝區溢出。

通常情況下,循環緩衝區代碼不能只在緩衝區滿時保釋。插入操作必須阻止並返回錯誤,否則必須阻塞更多空間。所以這是一個基於示例程序特定假設的特例。