this tutorial assumes you have already read the previous one.
once you have the basic frame up for your multi-processor aware application, you will now want to actualy DO something with it, and in general you will want to syncronise between the threads, so that they are not redoing work already done.
to do this you will need to use something called a Critical Section, or a Spinlock. this is what tells a thread to wait for another thread to finish doing what it says is a critical section (ie, reading from or writing to a file, or from a network stream).
the critical section can be anything from the entire thread, to the simple read/write functions only. the best way to do this is to just have the read/write functions as critical sections, as these are the only things that need waiting for and syncronising. everything else can run as it needs.
to define a critical section, plase this somewhere in your global declarations
CODE
CRITICAL_SECTION g_crit_sec;
this critical section will need initializing before it can be used.. so to do this, please the init function somewhere within your main function, BEFORE you create your threads
CODE
InitializeCriticalSectionAndSpinCount(&g_crit_sec, 0x80000400);
this initializes the critical section, and sets a spin count for it.
QUOTE
Spinning means that when a thread tries to acquire a critical section that is locked, the thread enters a loop, checks to see if the lock is released, and if the lock is not released, the thread goes to sleep
now that we have a critical section, and it has been set up, we now need to use it. within your thread proc, you will need to enter the thread at some point.
CODE
DWORD_PTR WINAPI threadMain(void* p) {
EnterCriticalSection(&g_crit_sec);
// do something like read/write a file
LeaveCriticalSection(&g_crit_sec);
//process the data you just read from a file
return 0;
}
EnterCriticalSection(&g_crit_sec);
// do something like read/write a file
LeaveCriticalSection(&g_crit_sec);
//process the data you just read from a file
return 0;
}
anything within the critical section will be processed in a queue, and anything outside the section will be processed as and when it is required to be, based on the number of threads there are and what you are actually doing.
example:
CODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
HANDLE *m_threads = NULL;
DWORD_PTR WINAPI threadMain(void* p);
DWORD_PTR GetNumCPUs() {
SYSTEM_INFO m_si = {0, };
GetSystemInfo(&m_si);
return (DWORD_PTR)m_si.dwNumberOfProcessors;
}
CRITICAL_SECTION g_crit_sec;
static int g_start = 0;
int wmain(int argc, wchar_t **args) {
DWORD_PTR c = GetNumCPUs();
m_threads = new HANDLE[c];
InitializeCriticalSectionAndSpinCount(&g_crit_sec, 0x80000400);
for(DWORD_PTR i = 0; i < c; i++) {
DWORD_PTR m_id = 0;
DWORD_PTR m_mask = 1 << i;
m_threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadMain, (LPVOID)i, NULL, &m_id);
SetThreadAffinityMask(m_threads[i], m_mask);
wprintf(L"Creating Thread %d (0x%08x) Assigning to CPU 0x%08x\r\n", i, (LONG_PTR)m_threads[i], m_mask);
}
return 0;
}
DWORD_PTR WINAPI threadMain(void* p) {
EnterCriticalSection(&g_crit_sec);
//without critical sections, this g_start might be referenced
//several times before it is increased, so several threads will
//be working with 0 or 100 or 200 or 400 at any one time,
//when you would rather one be working from 0, one working
//from 100, one from 200... etc.
//with the critical section, g_start is referenced, updated,
//and then the next thread is able to reference and update it,
//and so on, but never more than 1 thread at the same time.
int m_start = g_start;
g_start += 100;
LeaveCriticalSection(&g_crit_sec);
//do something with m_start
wprintf(L"g_start = %d\r\n", m_start);
return 0;
}
#include <windows.h>
#include <stdio.h>
HANDLE *m_threads = NULL;
DWORD_PTR WINAPI threadMain(void* p);
DWORD_PTR GetNumCPUs() {
SYSTEM_INFO m_si = {0, };
GetSystemInfo(&m_si);
return (DWORD_PTR)m_si.dwNumberOfProcessors;
}
CRITICAL_SECTION g_crit_sec;
static int g_start = 0;
int wmain(int argc, wchar_t **args) {
DWORD_PTR c = GetNumCPUs();
m_threads = new HANDLE[c];
InitializeCriticalSectionAndSpinCount(&g_crit_sec, 0x80000400);
for(DWORD_PTR i = 0; i < c; i++) {
DWORD_PTR m_id = 0;
DWORD_PTR m_mask = 1 << i;
m_threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadMain, (LPVOID)i, NULL, &m_id);
SetThreadAffinityMask(m_threads[i], m_mask);
wprintf(L"Creating Thread %d (0x%08x) Assigning to CPU 0x%08x\r\n", i, (LONG_PTR)m_threads[i], m_mask);
}
return 0;
}
DWORD_PTR WINAPI threadMain(void* p) {
EnterCriticalSection(&g_crit_sec);
//without critical sections, this g_start might be referenced
//several times before it is increased, so several threads will
//be working with 0 or 100 or 200 or 400 at any one time,
//when you would rather one be working from 0, one working
//from 100, one from 200... etc.
//with the critical section, g_start is referenced, updated,
//and then the next thread is able to reference and update it,
//and so on, but never more than 1 thread at the same time.
int m_start = g_start;
g_start += 100;
LeaveCriticalSection(&g_crit_sec);
//do something with m_start
wprintf(L"g_start = %d\r\n", m_start);
return 0;
}