Windows NT Systems
Programming
[ Home
| Syllabus |Course Notes]
Left Overs/Multiple Clients
Three Missed WIN32 APIs
SlideBodyHere
Missing Environment
SlideBodyHere
Deleting a Garbage Pointer
SlideBodyHere
Flat Naming Structure
SlideBodyHere
strncpy
SlideBodyHere
Reopen
SlideBodyHere
name2IP
SlideBodyHere
Multiple Clients = multiple threads
Why Threads?
- Unblocks user interface
- Simplify coding
- Handle high priority tasks
- Multiple CPUs
- Shared Address Space
- Less overhead than a process
Shared Address Space
- PROCESS GLOBAL: All threads
in a process share
- Global variables
volatile
- HEAP
- THREAD LOCAL: Each thread
has a private stack for local variables, function
calls.
- THREAD GLOBAL: WIN32 API also
supports global name space limited to a single thread
Thread States
- Running
Highest Priority Ready to Run Thread
- Ready to Run
- Waiting
- SLEEP
- Wait I/O
- Wait SOCKET
- Wait Synchronization
- Wait Event
- Wait SEMAPHORE
- Wait MUTEX
- WAIT CRITICAL
SECTION
Creating a Thread
Thread runs a special function with
one parameter.
Waiting for a thread to finish
WIN32 supports a general WaitFor
mechanism which works for many different kinds of events.
Single Thread/Process Global Variable
(Source Code is here)
// ...
#include <windows.h> // for defining HANDLE, Sleep
const int SLEEP_TIME = 1000;
int global1; // file global
void testThread(int* myId ); // thread prototype
void main()
{
HANDLE testHandle;
DWORD threadId;
int id = 1;
global1 = 33;
testHandle = CreateThread (0, // no child process cannot inherit
0, // gives same stack space as parent thread
(LPTHREAD_START_ROUTINE) TestThread,
&id, // Pass in one parameter
0, // Put into ready Q
&threadID); // system wide thread ID
Sleep (SLEEP_TIME); // sleep for one second - give thread a chance
global1 = 55;
WaitForSingleObject(testHandle, INFINITE);
}
void testThread(int* myId)
{
cout << "Started Thread ID: " << *myId << endl;
cout << "BEFORE: Global1 is: " << global1 << endl;
Sleep(2*SLEEP_TIME); // give main thread chance to change global1
cout << "AFTER: Global1 is: " << global1 << endl;
}
What will the output?
Multiple Threads/Global Variable
(Source Code is
here)
void testThread(DWORD myId);
void main()
{
HANDLE testHandles[2];
testHandles[0] = CreateThread(0,0,
(LPTHREAD_START_ROUTINE) testThread,
(void*)id, 0, &threadId);
id++;
testHandles[1] = CreateThread(0,0,
(LPTHREAD_START_ROUTINE) testThread,
(void*)id, 0, &threadId);
Sleep(SLEEP_TIME); // sleep for one second
WaitForMultipleObjects(2, testHandles, TRUE /* wait for all */, INFINITE);
}
// Separate file
//**** all threads started with this code will share the file global variable "global2"
int global2 = 12345;
void testThread(DWORD myId)
{
int id = myId;
cout << "Started Thread ID: " << id << endl;
cout << "BEFORE: Global2 is: " << global2 << endl;
global2 = id;
Sleep(id*SLEEP_TIME); // give main thread chance to change global1
cout << "AFTER: Global2 is: " << global2 << endl;
}
// What will be the output?
Thread Local Storage (TLS)
(Source Code is here)
DWORD tlsIndex;
void testThread(int* myId);
void main()
{
// . . .
tlsIndex = TlsAlloc();
testHandles[0] = CreateThread(0,0,
(LPTHREAD_START_ROUTINE) testThread,
&id, 0, &threadId);
Sleep(SLEEP_TIME); // give first thread a chance to save its parameter
//. . . separate file for thread code
extern DWORD tlsIndex;
void checkGlobal(int);
void testThread(int* myId)
{
int id = *myId;
int global3 = id;
TlsSetValue(tlsIndex, &global3);
checkGlobal(id);
}
void checkGlobal(int id)
{
int* tls;
tls = (int*) TlsGetValue(tlsIndex);
cout << "BEFORE: Global3 is: " << *tls << endl;
*tls = id*1000;
Sleep(id*SLEEP_TIME); // give main thread chance to change global1
cout << "AFTER: Global3 is: " << *tls << endl;
}
// What output?
Version 3 Multiple Server main program
(Source Code is here)
// Prototype to thread call
VOID talkToClient(VOID * cs);
int main()
{
// . . .
DWORD threadID;
HANDLE clientThread;
serverSocket = getServerSocket();
while (true) {
// accept a connection from a client
clientSocket=accept(serverSocket,
(LPSOCKADDR) &clientSockAddr,
&addrLen);
//. . .
clientThread = CreateThread (0, // no child process cannot inherit
0, // gives same stack space as parent thread
(LPTHREAD_START_ROUTINE) talkToClient,
(VOID *) clientSocket, // Pass in one parameter
0, // Put into ready Q
&threadID); // system wide thread ID
// . . .
}
}
VOID talkToClient(VOID *cs)
{
SOCKET socket2client=(SOCKET)cs;
NET_CONTROL request;
while(TRUE) // loop until killed
{
request = getControl(socket2client);
if (request == FAILURE) {
shutdown(socket2client, 2); // 2 shutdown send and receive
closesocket(socket2client);
ExitThread(0);
}
switch (request)
// . . .
Synchronization
- Efficient Waiting
- Fair Waiting
Critical Sections
VARIABLE:
- CRITICAL_SECTION critSec;
- GLOBAL to all threads
- Threads NOT processes
FUNCTIONS:
- InitializeCriticalSection(&critSec)
- Should be done only
once
- DO NOT COPY OR MOVE
- EnterCriticalSection
- Must be paired with
LeaveCriticalSection
- Must be well order wrt
LeaveCriticalSection
- Can be entered
multiple times by one thread
- LeaveCriticalSection
- DeleteCriticalSection
Critical Section Example (listing 6.4)
#include <windows.h>
//. . .
volatile INT count;
CRITICAL_SECTION critSec;
void CountThread(INT iterations)
{
//. . .
for (i=0; i<iterations; i++)
{
EnterCriticalSection(&critSec);
x=count;
x++;
count=x;
LeaveCriticalSection(&critSec);
}
}
void main(void)
{
// . . .
InitializeCriticalSection(&critSec);
for (i=0; i<numThreads; i++)
{
// create a thread and pass it the pointer
// to its "params" struct
handles[i]=CreateThread(0, 0,
(LPTHREAD_START_ROUTINE) CountThread,
(VOID *) 25000, 0, &threadID);
}
WaitForMultipleObjects(numThreads, handles,
TRUE, INFINITE);
DeleteCriticalSection(&critSec);
}
MUTEX (BINARY SEMAPHORE)
Can be used across processes by
naming it.
VARIABLE:
FUNCTIONS:
- CreateMutex(security, initial
state, name)
- name is system wide
- MAXPATH length
- can use "."
to generate qualified name
e.g. NTcourse.semaphore.writersMutex
- OpenMutex(access, inherit,
name)
to access an existing one. (can also use CreateMutex)
- WaitFor....
access granted if no body owns it, else block
- ReleaseMutex(handle)
so another can use it.
MUTEX example
void CountThread(INT iterations)
{
INT i;
INT x;
HANDLE mutex;
mutex = CreateMutex(0, FALSE, "counterMutex"); // only first thread actually creates it
for (i=0; i<iterations; i++)
{
WaitForSingleObject(mutex, INFINITE);
x=count;
x++;
count=x;
ReleaseMutex(mutex);
}
CloseHandle(mutex);
}
SEMAPHORES (counting ones)
The bounded buffer example is kind
of neat. p. 231--
Events
In automatic mode, like a MUTEX, in
manual can be used to release a number of waiting
processes/threads.
WaitFor...
Used to wait for many things (p.
257)
- threads to finish
- MUTEX
- SEMAPHORE
- EVENT
- changes in directory
- input on console
- process termination
Also allows a time out value.
- WAIT_FAILED: error
- WAIT_TIMEOUT: if timeout
expired
- WAIT_OBJECT_0: object signaled
- WAIT_ABANDONED: MUTEX object
owner died.
Multiple Objects can be waited for
Lab Assignment
see lab3
Copyright chris wild 1997.
For problems or questions regarding this web contact [Dr. Wild].
Last updated: September 08, 1997.