Tải bản đầy đủ - 0 (trang)
XI.4 Đồng bộ hóa hoạt động của các thread.

XI.4 Đồng bộ hóa hoạt động của các thread.

Tải bản đầy đủ - 0trang

#include

#include

#include

#include

#include

#include

#define MAX_THREADS 32

/* getrandom returns a random number between min and max, which must be in

integer range. */

#define getrandom( min, max ) ((rand() % (int)(((max) + 1) - (min))) + (min))

void main( void );

/* Thread 1: main */

void KbdFunc( void );

/* Keyboard input, thread dispatch */

void BounceProc( char * MyID );

/* Threads 2 to n: display */

void ClearScreen( void );

/* Screen clear */

void ShutDown( void );

/* Program shutdown */

void WriteTitle( int ThreadNum );

/* Display title bar information */

HANDLE hConsoleOut;

/* Handle to the console */

HANDLE hRunMutex;

/* "Keep Running" mutex */

HANDLE hScreenMutex;

/* "Screen update" mutex */

int ThreadNr=0;

/* Number of threads started */

CONSOLE_SCREEN_BUFFER_INFO csbiInfo; /* Console information */

void main()

/* Thread One */

{

/* Get display screen information & clear the screen.*/

hConsoleOut = GetStdHandle( STD_OUTPUT_HANDLE );

GetConsoleScreenBufferInfo( hConsoleOut, &csbiInfo );

ClearScreen();

WriteTitle( 0 );

/* Create the mutexes and reset thread count. */

hScreenMutex = CreateMutex( NULL, FALSE, NULL ); /* Cleared */

hRunMutex = CreateMutex( NULL, TRUE, NULL );

/* Set */

/* Start waiting for keyboard input to dispatch threads or exit. */

KbdFunc();

/* All threads done. Clean up handles. */

CloseHandle( hScreenMutex );

CloseHandle( hRunMutex );

CloseHandle( hConsoleOut );

}

void ShutDown( void )

{

while ( ThreadNr > 0 )

{



/* Shut down threads */



396



/* Tell thread to die and record its death. */

ReleaseMutex( hRunMutex );

ThreadNr--;

}

/* Clean up display when done */

WaitForSingleObject( hScreenMutex, INFINITE );

ClearScreen();

}

void KbdFunc( void )

/* Dispatch and count threads. */

{

int

n, KeyInfo;

for(n=0;n
do

{

KeyInfo = _getch();

} while( tolower( KeyInfo ) != 'q' );

ShutDown();

}

void BounceProc( char *MyID )

{

char

MyCell, OldCell;

WORD

MyAttrib, OldAttrib;

char

BlankCell = 0x20;

COORD Coords, Delta;

COORD Old = {0,0};

DWORD Dummy;

/* Generate update increments and initial display coordinates. */

srand( (unsigned) *MyID * 3 );

Coords.X = getrandom( 0, csbiInfo.dwSize.X - 1 );

Coords.Y = getrandom( 0, csbiInfo.dwSize.Y - 1 );

do

{Delta.X = getrandom( -3, 3 );

Delta.Y = getrandom( -3, 3 );

} while ((Delta.X*Delta.X+Delta.Y*Delta.Y)==0);

/* Set up "happy face" & generate color attribute from thread number.*/

if( *MyID > 16)

MyCell = 0x01;

/* outline face */

else

MyCell = 0x02;

/* solid face */

MyAttrib = *MyID & 0x0F;

/* force black background */

do

{

/* Wait for display to be available, then lock it. */

WaitForSingleObject( hScreenMutex, INFINITE );

/* If we still occupy the old screen position, blank it out. */

ReadConsoleOutputCharacter( hConsoleOut, &OldCell, 1, Old, &Dummy );



397



ReadConsoleOutputAttribute( hConsoleOut, &OldAttrib, 1, Old, &Dummy );

if (( OldCell == MyCell ) && (OldAttrib == MyAttrib))

WriteConsoleOutputCharacter( hConsoleOut, &BlankCell, 1, Old, &Dummy);

/* Draw new face, then clear screen lock */

WriteConsoleOutputCharacter( hConsoleOut, &MyCell, 1, Coords, &Dummy);

WriteConsoleOutputAttribute( hConsoleOut, &MyAttrib, 1, Coords, &Dummy);

ReleaseMutex( hScreenMutex );

/* Increment the coordinates for next placement of the block. */

Old.X = Coords.X;

Old.Y = Coords.Y;

Coords.X += Delta.X;

Coords.Y += Delta.Y;



//



//



/* If we are about to go off the screen, reverse direction */

if( Coords.X < 0 || Coords.X >= csbiInfo.dwSize.X )

{

Delta.X = -Delta.X;

Beep( 400, 50 );

}

if( Coords.Y < 0 || Coords.Y > csbiInfo.dwSize.Y )

{

Delta.Y = -Delta.Y;

Beep( 600, 50 );

}

}

/* Repeat while RunMutex is still taken. */

while ( WaitForSingleObject( hRunMutex, 75L ) == WAIT_TIMEOUT );



}

void WriteTitle( int ThreadNum )

{

char NThreadMsg[80];

sprintf( NThreadMsg, "Threads running: %02d. Press 'Q' to quit.", ThreadNum);

SetConsoleTitle( NThreadMsg );

}

void ClearScreen( void )

{

DWORD dummy;

COORD Home = { 0, 0 };

FillConsoleOutputCharacter( hConsoleOut, ' ', csbiInfo.dwSize.X *

csbiInfo.dwSize.Y, Home, &dummy );

}



XI.4.2. Bài toán sản-xuất & tiêu-thụ (Producer & Consumer )



398



#include

#include

#define BUFSIZE 5

int SharedBuffer[BUFSIZE];

int head,tail;

int count;

HANDLE hMutex;

HANDLE NotFull, NotEmpty;

void Producer()

{int i;

for (i=20; i>=0; i--) {

while(1) {

WaitForSingleObject(hMutex,INFINITE);

if (count == BUFSIZE) {/* buffer is full*/

ReleaseMutex(hMutex);

// wait until buffer is not full

WaitForSingleObject(NotFull,INFINITE);

continue; // back to loop to test buffer state again

}

// got mutex and buffer is not FULL, break out of while loop

break;

}

// got Mutex, buffer is not full, producing data

cout << "Produce: " << i << endl;

SharedBuffer[tail] = i;

tail = (tail+1) % BUFSIZE;

count++;

ReleaseMutex(hMutex); // end critical section

PulseEvent(NotEmpty); // wake up Consumer thread

}

}

void Consumer()

{int result;

while (1) {

WaitForSingleObject(hMutex,INFINITE);

if (count == 0) { // nothing to consume

ReleaseMutex(hMutex); // release lock to wait

// wait until buffer is not empty

WaitForSingleObject(NotEmpty,INFINITE);

}

else if (SharedBuffer[head] == 0) {// test for end of data token

cout << "Consumed 0: end of data" << endl;

ReleaseMutex(hMutex); // end critical section

ExitThread(0);

}

else { // got Mutex, data in buffer, start consuming

result = SharedBuffer[head];



399



cout << "Consumed: " << result << endl;

head = (head+1) % BUFSIZE;

count--;

ReleaseMutex(hMutex); // end critical section

PulseEvent(NotFull); // wake up producer thread

}

}

}

void main()

{

HANDLE Thread[2];

DWORD ThreadID;

count = 0; head = 0; tail = 0;

hMutex = CreateMutex(NULL,FALSE,NULL);

NotFull = CreateEvent(NULL,TRUE,FALSE,NULL);

NotEmpty = CreateEvent(NULL,TRUE,FALSE,NULL);

Thread[0] = CreateThread (NULL, 0,

(LPTHREAD_START_ROUTINE)

Producer, NULL, 0, (LPDWORD)&ThreadID);

Thread[1] = CreateThread (NULL, 0,

(LPTHREAD_START_ROUTINE)

Consumer, NULL, 0, (LPDWORD)&ThreadID);

WaitForMultipleObjects(2,Thread,TRUE,INFINITE);

}



400



XI.5 Java thread.

Java là ngôn ngữ cho phép lập trình dựa trên thread. Các thread chạy song

song và chúng có thể cùng truy cập vào biến chung. Vì vậy nguyên lý lập

trình thread cho Java cũng giống như với C. Và như vậy sẽ có tất cả các sự

kiện như Race, Deadlock....

Phần sau đây dành để nêu ra một vài ví dụ về các hiện tượng trên.

XI.5.1. Hiện tượng Race.

XI.5.1.A



Nghịch lý “Bán Vé”.



Hiện tượng race xuất hiện khi các thread cùng truy cập để sửa đổi nội dung

của một biến. Trong trình sau các thread tranh nhau tăng giá trị biến chung,

Count, tới 10. Đáng nhẽ ra, chỉ được tăng lên đến 10, nhưng do hiện tượng

race mà giá trị có thể tăng lên nhiều hơn.

// RaceDemo.java

class SelfishRunner extends Thread {

static int tick = 0;

// biến dùng chung

private int num;

public SelfishRunner(int num) { this.num = num; }

public void run() {

while (tick < 10) {

try { Thread.sleep(1); } catch(Exception e) { }

tick++;

System.out.println("Thread " + num + ", tick = " + tick);

}

}

}

public class RaceDemo {

private final static int N = 3;

public static void main(String[] args) {

SelfishRunner[] runners = new SelfishRunner[N];

for (int i = 0; i < N; i++) {

runners[i] = new SelfishRunner(i);

runners[i].setPriority(2);

}

for (int i = 0; i < N; i++) runners[i].start();

}

}



401



XI.5.1.B



Hiện tượng Race khi khơng có đồng bộ.



Hiện tượng race có thể xuất hiện khi dừng chương trình bằng lệnh

System.exit(0);



Do khơng có đồng bộ nào được thiết lập mà khi lệnh trên bắt đầu có hiệu lực

(ở một thread nào đó) thì các thread khác vẫn hoạt động.

Chương trình sau tìm và in ra các số nguyên tố liên tiếp. Để dừng trình, từ

bàn phím, ta cần ra lệnh stop. Chúng ta thấy rằng, trong thời gian thực hiện

lệnh “System.exit(0)”, vẫn có vài số nguyên tố, do thread khác, tạo ra.

// PrimeCount.java

import java.io.*;

public class PrimeCount implements Runnable

{ long total = 0;

// biến global

static Thread Calculate;

static Thread Listen;

public static void main (String[] args)

{

PrimeCount e = new PrimeCount();

Calculate

= new Thread(e);

Listen

= new Thread(e);

Calculate.start();

Listen.start();

}

public void run()

{ Thread currentThread = Thread.currentThread();

if (currentThread == Calculate) calculatePrimes();

else if (currentThread == Listen) listenForStop();

}

public void calculatePrimes()

{ int n = 1;

while (true)

{ n++;

boolean isPrime = true;

for (int i = 2; i < n; i++) if ((n / i) * i == n) { isPrime = false; break; }

if (isPrime) { total++; System.out.println(n); }

}

}

private void listenForStop()

{

BufferedReader input = new BufferedReader(new

InputStreamReader(System.in));

String line = "";

while (!line.equals("stop"))

{ try { line = input.readLine(); } catch (IOException exception) {}

}

System.out.println("Found " + total + " prime numbers before you said stop");



402



System.exit(0);

}

}



XI.5.2. Đồng bộ hóa thread.

Trình sau đây

// Synch.java

class Callme {

void call(String msg)

{ System.out.print("["+msg);

try { Thread.sleep((int) (100*Math.random())); } catch(Exception e) { }

System.out.println("]");

}

}

class Caller implements Runnable

{ String msg;

Callme Target;

public Caller(Callme t, String s)

{ Target = t;

msg = s;

new Thread(this).start();

}

public void run() {

Target.call(msg);

}

}

class Synch {

public static void main(String args[]) {

Callme Target = new Callme();

new Caller(Target, "Hello");

new Caller(Target, "Synchronized");

new Caller(Target, "World");

}

}



tạo ra 3 thread và mỗi thread phải thực hiện lệnh in ra màn hình một từ nằm

trong dấu ngoặc [ ].

void call(String msg)

{ System.out.print("["+msg);

try { Thread.sleep((int) (100*Math.random())); } catch(Exception e) { }

System.out.println("]");

}



Tuy nhiên, kết quả chạy trình khơng giống như chúng ta mong muốn là

[Hello]

[World]

[Synchronized]



mà chương trình in ra các dòng chữ sau

403



[Hello[Synchronized[World]

]

]



Nguyên nhân dẫn đến sự kiện trên là (do chạy song song nên) các thread đã

tranh nhau in ra màn hình, khiến cho thứ tự in các ngoặc ra không đúng. Vậy

đoạn lệnh cần phải được thực hiện theo kiểu CS. Trong Java một đoạn lệnh

như vậy được thông báo bằng chỉ thị /*synchronized*/

// Synch.java

class Callme { // Check synchronized and unsynchronized methods

synchronized void call(String msg)

{ System.out.print("["+msg);

try { Thread.sleep((int) (100*Math.random())); } catch(Exception e) { }

System.out.println("]");

}

}

class Caller implements Runnable

{ String msg;

Callme Target;

public Caller(Callme t, String s)

{

Target = t;

msg = s;

new Thread(this).start();

}

public void run() {

Target.call(msg);

}

}

class Synch {

public static void main(String args[]) {

Callme Target = new Callme();

new Caller(Target, "Hello");

new Caller(Target, "Synchronized");

new Caller(Target, "World");

}

}



Kết quả in ra là

[Hello]

[World]

[Synchronized]



XI.5.2.A



Lời giải cho nghịch lý “Bán Vé”.



Để tránh hiện tượng Race chúng ta cần phải chuyển đoạn lệnh

while (tick < 10) {

try { Thread.sleep(1); } catch(Exception e) { }



404



tick++;

System.out.println("Thread " + num + ", tick = " + tick);

}



thành CS.

Điểu này có thể được thực hiện nhờ chỉ thị

synchronized(CS)



như sau

// NoRaceDemo.java

class SelfishRunner extends Thread {

static int tick = 0;

private int num, i;

public SelfishRunner(int num) { this.num = num; }

public void run() {

int CS;

synchronized(CS)

{ while (tick < 10) {

try { Thread.sleep(1); } catch(Exception e) { }

tick++;

System.out.println("Thread " + num + ", tick = " + tick);

}

}

}

}

public class NoRaceDemo {

private final static int N = 3;

public static void main(String[] args) {

SelfishRunner[] runners = new SelfishRunner[N];

for (int i = 0; i < N; i++) {

runners[i] = new SelfishRunner(i);

runners[i].setPriority(2);

}

for (int i = 0; i < N; i++) runners[i].start();

}

}



405



XI.5.3. Hiện tượng deadlock.

XI.5.3.A



“Tán Tỉnh”.



Hai gã, Tom và John, mỗi người đều đã có bạn gái. Lucy là bạn gái của Tom

và Jane là bạn gái của John. Sau một cuộc làm quen “chớp nhoáng”, Tom và

John, người này bí mật gọi điện cho bạn gái của người kia để hẹn gặp riêng.

Điều gì sẽ xảy ra nếu họ, cả Tom và John, đều không thể gọi được điện thoại

để hủy bỏ cuộc đi chơi đã hẹn trước với bạn gái của mình (mà cũng có thể là

họ khơng muốn gọi?!). Lẽ dĩ nhiên các cơ gái thì chung thủy, họ sẽ khơng

thể nhận lời khi đã có hẹn, còn các chàng trai thì nhất định phải đuổi theo cái

mới “?”. Họ, cả 4 người, sẽ không thể ra khỏi được hiện trạng “deadlock”

này!

Trình sau đây minh họa hiện tượng deadlock xuất hiện trong câu chuyện vui

nói trên.

// DeadlockDemo.java

public class DeadlockDemo

{

public static void main(String args[])

{

String girl1 = "Jane";

String girl2 = "Lucy";

Boy boy1 = new Boy("John", girl1, girl2);

Boy boy2 = new Boy("Tom", girl2, girl1);

boy1.start();

boy2.start();

}

}

class Boy extends Thread

{

String name, olddate, newdate;

Boy(String name, String olddate, String newdate)

{

this.name=name;

this.olddate=olddate;

this.newdate=newdate;



406



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

XI.4 Đồng bộ hóa hoạt động của các thread.

Tải bản đầy đủ ngay(0 tr)

×