Friday, June 5, 2015

[C / C++] Cross-Platform pratik dosya yaratma & okuma sınıfları (Linux/Windows/MacOs)

Merhaba arkadaşlar. Gerek iş, gerek okul nedeniyle blogu uzun zamandır güncelleyemiyorum. Bugün pratik dosya yaratmanıza ve okumanıza olanak sağlayacak iki adet sınıf paylaşacağım. Bu iki sınıfı kullanarak sadece birkaç satır ile dosya okuyabilecek, yaratabilecek, veya dosyaya yazabileceksiniz. Öncelikle, kullanacağımız sınıflar şunlar;

FileReader.h


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#pragma once

#ifndef WIN32
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
#else
 #include <io.h>
#endif

#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>


class FileReader
{
public:
 FileReader(char * path);
 ~FileReader();
 bool Open();
 char * GetContents() const { return buf; }
 char * GetAt(int index) const { return &buf[index]; }
 bool CopyTo(char * path);
 long   GetSize()  const { return _size; }
 void PrintError();
private:
 /* File handle */
 int  _fhandle;
 /* File size */
 long _size;
 /* File path */
 char * m_szPath;
 /* File content */
 char * buf;
 bool ReadFileContent();
};

/* Constructor */
FileReader::FileReader(char * path) : m_szPath(path),_size(-1),_fhandle(-1) {}

/* Destructor */
FileReader::~FileReader()
{
 if (_fhandle != -1)
 {
  delete[] buf;
  close(_fhandle);
 }
}

bool FileReader::Open()
{
 _fhandle = open(m_szPath, O_RDWR);

 if (_fhandle > -1)
 {
  if (!ReadFileContent())
   return false;
  return true;
 }
 return false;
}

bool FileReader::ReadFileContent()
{
 if (_fhandle == -1)
  return false;
 struct stat stat_buf;
 int rc = stat(m_szPath, &stat_buf);
 if (rc == 0)
 {
  _size = stat_buf.st_size;
  buf = new char[_size];
  read(_fhandle, &buf[0], _size);
  return true;
 }
 else
  return false;
}

bool FileReader::CopyTo(char * szPath)
{
 if (_fhandle > -1)
 {
  int _fhandle2 = open(szPath, O_CREAT | O_EXCL | O_RDWR);
  if (_fhandle2 > -1)
  {
   if (write(_fhandle2, GetContents(), GetSize()) == GetSize())
   {
    close(_fhandle2);
    return true;
   }
   close(_fhandle2);
  }
  return false;
 }
 return false;
}

void FileReader::PrintError()
{
 perror("FileReader");
}

FileBuilder.h


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#pragma once


#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>

#ifndef WIN32
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
#else
 #include <io.h>
#endif

#define SEEK_CUR    1
#define SEEK_END    2
#define SEEK_SET    0

class FileBuilder
{
public:
 FileBuilder(char * ,bool);
 ~FileBuilder();
 void PrintError();
 bool Create();
 bool Append(char * , long );
 bool Append(FileReader&);
private:
 bool _overwrite;
 /* File handle */
 int  _fhandle;
 /* File size */
 long _size;
 /* File path */
 char * m_szPath;
};

FileBuilder::FileBuilder(char * szPath, bool overwrite = true) :m_szPath(szPath), _size(-1), _fhandle(-1), _overwrite(overwrite)
{
}

bool FileBuilder::Create()
{
 if (!_overwrite)
  _fhandle = open(m_szPath, O_CREAT | O_EXCL | O_RDWR, 0777);
 else
  _fhandle = open(m_szPath, O_CREAT | O_TRUNC | O_RDWR, 0777);
 if (_fhandle > -1)
 {
  
  if (!_overwrite && errno == EEXIST)
   return false;
  return true;
 }
 return false;
 
}

bool FileBuilder::Append(char * val, long size)
{
 if (_fhandle == -1)
  return false;
 lseek(_fhandle, 0, SEEK_END);
 return write(_fhandle, val, size) == size;
}

bool FileBuilder::Append(FileReader& reader)
{
 if (_fhandle == -1)
  return false;
 lseek(_fhandle, 0, SEEK_END);
 return write(_fhandle, reader.GetContents(), reader.GetSize()) == reader.GetSize();
}

void FileBuilder::PrintError()
{
 perror("FileBuilder");
}


FileBuilder::~FileBuilder()
{
 if (_fhandle != -1)
 {
  close(_fhandle);
 }
}

Yukardaki sınıfları FileReader.h ve FileBuilder.h olarak kaydedin ve kullanmak istediğiniz projeye include edin.(veya aşagıdan dosyaları indirebilirsiniz)

Sınıfların kullanımı ;

FileReader :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include "stdafx.h"
#include "FileReader.h"
#include "FileBuilder.h"
#include <iostream>

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
 FileReader fr("C:\\demo.txt"); // c:\\demo.txt, açılmak istenen dosyanın konumu + adı
 if (fr.Open()) // open fonksiyonunu çağırarak dosyayı açalım
  cout << "Dosya başarı ile açıldı.\n";
 else
  fr.PrintError(); // Eğer dosya açılamadıysa, ekrana sebebini belirten hata mesajını yazdıralım.

 char * buf = fr.GetContents(); // Dosyanın içeriğini getir (char array pointer)
 // fr.GetSize() ile dosya boyutuna erişilebilir

 fr.CopyTo("c:\\demo2.txt"); // Açılan dosyayı verilen konuma kopyalar.
}

FileBuilder :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include "stdafx.h"
#include "FileReader.h"
#include "FileBuilder.h"
#include <iostream>

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
 // İlk parametre yaratılacak dosya konumu + adı, ikincisi eğer dosya mevcutsa üzerine yazılıp yazılmayacağı
 FileBuilder fb("c:\\file.txt",true); 
 
 if (fb.Create()) // Eğer dosya yaratıldıysa
  cout << "Dosya yaratıldı. \n";
 else
  fb.PrintError(); // Yaratılamadıysa, nedenini bildiren hata mesajını yazdır

 //fb.Append(fr);

 char * m = new char[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'};
 if (fb.Append(&m[0], 7)) // Append fonksiyonu ile dosyaya veri ekle
  cout << "Ekleme başarılı.\n";

 //Append fonksiyonunu kullanarak aynı zamanda FileReader objelerini de ekleyebilirsiniz.
 
 getchar();
 return 0;
}

Bugünlük bu kadar :) Sağlıcakla kalın.

FileReader.h & FileWriter.h  = BURADAN

Wednesday, January 21, 2015

[C++] CPU Scheduler Simulator (İşlemci zamanlayıcısı simülatörü)

Evet arkadaşlar, bugün üzerinde duracağımız konu, konu olmaktan ziyade daha çok bir proje açıklaması klasmanına girecek. Dersi alan arkadaşların bildiği üzere,bu proje bu dönemki "Operating Systems" dersinin projesi. Projenin temel amacı, işlemci zamanlayıcısının çalışma mantığını kavramak ve işlemci zamanlayıcısının kullandığı zamanlama algoritmalarını pratikte gözlemleyebilmekti. İsterseniz ağırdan başlayalım.

Öncelikle, projenin gereksinimlerinden birisi linux platformunda çalışması olduğundan, ben şahsi olarak bu proje için hem Windows, hem de Linux alternatifi bulunan Code::Blocks adlı ufak IDE'yi kullandım. Code::Blocks ile ilgili detaylı bilgiyi ve indirme linklerine buradan erişebilirsiniz.

Konu ile aşinalık kazanmak açısından önce bir işlemci zamanlayıcısının ne iş yaptığından ve işlemlerin CPU zamanını nasıl paylaştığından bahsedelim.

İşlemci dediğimiz bilgisayar parçası, verileri sequential yani sıralı olarak işlediğinden dolayı, normalde bir işlemcinin aynı anda birden fazla işlem çalıştırma olanağı yoktur. Eskiden kullanılan sistemlerde, işletim sistemleri sadece 1 işlem çalıştırmaya olanak tanıyordu. Multitasking dediğimiz olgu, işletim sistemlerine işlemci zamanlayıcısı eklenmeye başlamasıyla beraber hayatımıza girdi.

CPU Scheduler, yani işlemci zamanlayıcısı, bir işletim sisteminin sahip olduğu temel bileşenlerden birisidir. Bu bileşenin görevi, işlemci zamanını sistemde o an için çalışmakta olan işlem ve iş parçacıkları arasında paylaştırmaktır. Bu zamanlama sistemi için, her işlemin çalışma durumunu belirten 5 durum vardır.


Figür 1.0 - İşlemlerin zamanlayıcı içerisindeki durumları

New, yani yeni durumu, program çalışmaya başladığı andaki durumdur. Yeni yaratılan her işlem, zamanlayıcı tarafından kabul edilir ve ready, yani hazır durumuna geçerler. Hazır durumunda bulunan işlemler, zamanlayıcının kendilerine işlemciyi kullanmak için sıra vermesini beklerler, sıraları geldiği an running yani çalışmakta durumuna geçerler. Eğer işlem, herhangi bir I/O operasyonu gerçekleştirirse (örneğin hard diskten veri okuma gibi) waiting yani bekleme durumuna geçer ve I/O operasyonu bitip interrupt gelene kadar zamanlayıcı o işlemi pas geçer. En son durum olan terminated yani sonlandırılma durumu ise biten bir işlemin zamanlayıcı kuyruğundan silinmesidir.

İşlemlerin zamanlayıcı içerisindeki durumlarını öğrendiğimize göre, zamanlayıcının işlemler arası nasıl geçiş yaptığına göz atalım.

Zamanlayıcı, daha önce de değindiğim üzere işlemcinin zamanını işlemler arasında paylaştırarak, kullanıcıda sanki bütün işlemler aynı anda çalışıyormuş hissi yaratır. Aslında olan şey eldeki işlem gücünün paylaştırılmasından ibarettir, fakat bu çok hızlı gerçekleştiğinden dolayı sanki işlemci gerçekten aynı anda bütün işlemleri işliyormuş gibi gelir.

Zamanlayıcının işlemler arası yaptığı geçişe context switch denir ve context switch sırasında kendine ayrılan süre biten işlem ready durumuna, sırası gelen işlem ise running durumuna geçer. Bu işlem sırasında ready durumuna geçirilen işlemin o anki durumunu içeren bütün bilgi(stack, registerlar, işlem içerisinde en son kalınan yer(program counter) vesaire) Process Context Block yani kısaca PCB içerisine kaydedilir, ve running durumuna getirilen işlemin daha önceden kaydedilen PCB'si geri yüklenir. Bu sayede işlemler arası geçiş yapılırken volatile olan verinin kaybı yaşanmaz.


Zamanlayıcı, işlemleri sıraya koyarken ve bir sonraki turda kimin çalışacağına karar verirken belirli başlı algoritmalar kullanır. Bunlardan bazıları şunlardır;

1-) First come, first served (FCFS) (İlk gelen hizmeti ilk alır)

2-) Shortest job first(SJF)                (En kısa işi olan hizmeti ilk alır)

3-) Priority scheduling (PS)            (Öncelik sırası en yüksek olan hizmeti ilk alır)

4-) Round - robin scheduling (RRS)

Şimdi bu algoritmaların çalışma prensiplerinden bahsedelim.

First come first served

Bu zamanlama algoritmasının mantığını bir banka kuyruğuna benzetebiliriz. İşlemler kuyruğa sokulur ve -işlemin işinin ne kadar uzun süreceği gibi değerlere bakılmaksızın- önce gelen hizmeti ilk alır.

Shortest job first

Bu methodda ise, zamanlayıcı kuyrukta hazırda bekleyen işlemler arasından her zaman işi en kısa olanı seçer. Bu methodun preemptive ve non-preemptive modelleri bulunmaktadır.

Preemptive modelinde, eğer bir işlem running durumundayken, yeni bir işlem kuyruğa eklenirse ve eklenen işlemin süresi, running durumundaki işlemin kalan süresinden daha kısaysa, yeni gelen işlem running durumuna geçirilir ve şuan çalışmakta olan işlem ise ready durumuna getirilir. Yani, zamanlayıcı bu modda her zaman en kısa süresi olana öncelik tanır.

Non-preemptive modelinde ise, yeni bir işlem geldiğinde şuan çalışan işlem hiçbir şekilde duraksatılmaz. Yeni gelen işlem kuyruğa eklenir ve şuan çalışmakta olan işlemin sırasının bitmesini bekler.

Priority Scheduling

Bu method ile çalışan bir zamanlayıcı, işlemlere öncelik değerine göre sıra verir. Her işlemin sahip olduğu bir priority değeri bulunmakla beraber, priority'si yüksek olan işlemler, düşük olan işlemlere karşı baskındır. Bu methodun dezavantajı starvation olmakla beraber eğer priority'si yüksek işlemler çoğunluktaysa, prioritysi düşük olan işlemler zamanlayıcı tarafından hiç dispatch edilmeyebilir.

Bu methodun da preemptive ve non-preemptive modu bulunmakla beraber SJF'deki mantıkla aynı çalışır.

Round robin scheduling

Son methodumuzda, her işlem zamanlayıcı tarafından eşit zaman aralıkları ile çalıştırılır. Bu zaman aralığına time quantum denir.



Terminoloji kısmını bitirdiğimize göre, artık kod kısmına geçebiliriz. Projenin dosya okuma vesaire gibi temel kısımlarını konuyu kısa tutmak açısından atlıyorum.

İşlemleri tutmak için kullandığım struct ve array tanımlamaları;




 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define MAX_PROCESS_ENTRY 255
int m_iTimeQuantum = 2;
int m_iProcessEntryCount = 0;

struct processEntry
{
    // Constant values
    int iBurstTime,iArrivalTime,iPriority;
    // Varies every calculation
    int iIndex,iTickTime, iWaitingTime;
    int iSwitchTime;
    bool isDone;


    void Reset()
    {
     iTickTime = 0;
     iWaitingTime = 0;
     iSwitchTime = 0;
     isDone = false;
    }
} ;

processEntry m_arProcessEntries[255];

Methodların tanımlamaları ;




  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
double SimulatorEngine::doFCFS()
{
 
  double avg = 0;

  for(int i = 0; i < m_uiProcessCount; i++)
  {
            /*
            If this process is arrived while another one is
            in his burst, incoming process *has to* wait until
            current process finishes its' burst.

            So we have to calculate waiting time according to that.
            */
            if(tickedTime >  processArray[i].iArrivalTime)
                waitingTime = (tickedTime -  processArray[i].iArrivalTime);
            else
                waitingTime = tickedTime;
            // Increase total tick time
            tickedTime +=  processArray[i].iBurstTime;
            // Assign calculated waiting time to process entry
            processArray[i].iWaitingTime = waitingTime;
            // Increase total waiting time
            avg += waitingTime;

    }
    // Divide total waiting time to process count to calculate the *average*
    avg /= m_uiProcessCount;
    return avg;
}

double SimulatorEngine::doPS()
{
    double avg = 0;
    int totalTick = getTotalBurst();

    if(m_isPreemptive)
    {
        do
        {
           int currentProcessPriority = INT_MAX;
           if(currentProcess != -1)
                currentProcessPriority = processArray[currentProcess].iPriority;

                // Temporary variables
           int highestProcIndex = -1;
           int highestPriority = INT_MAX;
                    // Determine new current process
           for(int i = 0; i < m_uiProcessCount; i++)
           {
                        // We're not interested in already finished process(es).
               if(processArray[i].isDone)
                    continue;
                        // The process is not arrived yet, so we're skipping it.
               if(processArray[i].iArrivalTime > tickedTime)
                    continue;
                        // Check if process has higher priority than current highest one
               if(processArray[i].iPriority < highestPriority)
               {
                    highestPriority = processArray[i].iPriority;
                    highestProcIndex = i;
               }
           }
                 // Context switch occurs if and only if shortest remaining job has a shorter remaining time.
            if(highestPriority < currentProcessPriority|| (currentProcess != -1 && processArray[currentProcess].isDone))
                currentProcess = highestProcIndex;


            for(int i = 0; i < m_uiProcessCount; i++)
            {
                // We're not gonna tick waiting time for current process
                if(i == currentProcess)
                    continue;
                // Not arrived yet, skip
                if(processArray[i].iArrivalTime > tickedTime)
                    continue;
                // Process is finished, skip
                if(processArray[i].isDone)
                    continue;

                // In this case, process is waiting and we should tick the waiting time
                processArray[i].iWaitingTime++;
                avg++;
            }
                // Increase the tick amount of current process and determine whether it's done or not.
            if(++processArray[currentProcess].iTickTime == processArray[currentProcess].iBurstTime)
                processArray[currentProcess].isDone = true;
            }
            while(++tickedTime < totalTick);
    }
    else
    {
        do
        {
            // Check whether current process is finished his job
            if(currentProcess == -1 || (currentProcess != -1 && processArray[currentProcess].isDone))
            {
                // Temporary variables
                int highestProcIndex = -1;
                int highestPriority = INT_MAX;
                // Determine new current process
                for(int i = 0; i < m_uiProcessCount; i++)
                {
                    // We're not interested in already finished process(es).
                    if(processArray[i].isDone)
                        continue;
                    // The process is not arrived yet, so we're skipping it.
                    if(processArray[i].iArrivalTime > tickedTime)
                        continue;
                    // Check if process has higher priority than current highest one
                    if(processArray[i].iPriority < highestPriority)
                    {
                        highestPriority = processArray[i].iPriority;
                        highestProcIndex = i;
                    }
                }
                // We've picked next available highest priority process.
                currentProcess = highestProcIndex;
            }

            for(int i = 0; i < m_uiProcessCount; i++)
            {
                // We're not gonna tick waiting time for current process
                if(i == currentProcess)
                    continue;
                // Not arrived yet, skip
                if(processArray[i].iArrivalTime > tickedTime)
                    continue;
                // Process is finished, skip
                if(processArray[i].isDone)
                    continue;

                // In this case, process is waiting and we should tick the waiting time
                processArray[i].iWaitingTime++;
                avg++;
            }
            // Increase the tick amount of current process and determine whether it's done or not.
            if(++processArray[currentProcess].iTickTime == processArray[currentProcess].iBurstTime)
                processArray[currentProcess].isDone = true;
        }
        while(++tickedTime < totalTick);
    }


    return avg / m_uiProcessCount;
}


double SimulatorEngine::doSTS()
{
    double avg = 0;
    int totalTick= getTotalBurst();

    if(m_isPreemptive)
    {
      do
      {
            int shortestRemainingTime = INT_MAX;
            int currProcRemTime = INT_MAX;
            int shortestRemainingIndex = -1;


            if(currentProcess != -1)
                currProcRemTime = processArray[currentProcess].iBurstTime - processArray[currentProcess].iTickTime;

            for(int i = 0; i < m_uiProcessCount; i++)
            {
                if(i == currentProcess)
                    continue;
                // Not arrived yet
                if(processArray[i].iArrivalTime > tickedTime)
                    continue;
                // Already finished
                if(processArray[i].isDone)
                    continue;

                if((processArray[i].iBurstTime - processArray[i].iTickTime) < shortestRemainingTime)
                {
                    shortestRemainingTime = (processArray[i].iBurstTime - processArray[i].iTickTime);
                    shortestRemainingIndex = i;
                }
            }
            // Context switch occurs if and only if shortest remaining job has a shorter remaining time.
            if(shortestRemainingTime < currProcRemTime || (currentProcess != -1 && processArray[currentProcess].isDone))
                currentProcess = shortestRemainingIndex;

                // Tick the waiting process(es)
            for(int i = 0; i < m_uiProcessCount; i++)
            {
                    // We're not gonna tick waiting time for current process
                    if(i == currentProcess)
                        continue;
                    // Not arrived yet, skip
                    if(processArray[i].iArrivalTime > tickedTime)
                        continue;
                    // Process is finished, skip
                    if(processArray[i].isDone)
                        continue;

                    // In this case, process is waiting and we should tick the waiting time
                    processArray[i].iWaitingTime++;
                    avg++;
            }
                // Increase the tick amount of current process and determine whether it's done or not.
            if(++processArray[currentProcess].iTickTime == processArray[currentProcess].iBurstTime)
                    processArray[currentProcess].isDone = true;
      }
      while(++tickedTime < totalTick);

    }
    else
    {
        for(int i = 0; i < m_uiProcessCount; i++)
        {
                /*
                If this process is arrived while another one is
                in his burst, incoming process *has to* wait until
                current process finishes its' burst.

                So we have to calculate waiting time according to that.
                */
                if(tickedTime > processArray[i].iArrivalTime)
                    waitingTime = (tickedTime - processArray[i].iArrivalTime);
                else
                    waitingTime = tickedTime;
                // Increase total tick time
                tickedTime += processArray[i].iBurstTime;
                // Assign calculated waiting time to process entry
                processArray[i].iWaitingTime = waitingTime;
                // Increase total waiting time
                avg += waitingTime;
        }
    }
    // Divide total waiting time to process count to calculate the *average*
    return avg /= m_uiProcessCount;
}

double SimulatorEngine::doRRS()
{
    double avg = 0;
    int totalTick= getTotalBurst();
    //cout << "Time Quantum : " << m_uiTimeQuantum << endl;

    // Initial waiting times
   for(int i = 0; i < m_uiProcessCount; i++)
    {
        processArray[i].iWaitingTime +=  processArray[i].iArrivalTime % m_uiTimeQuantum;
        avg += processArray[i].iArrivalTime % m_uiTimeQuantum;
    }
    do
    {
         int roundTime = 0;
         for(int i = 0; i < m_uiProcessCount; i++)
         {
            if(processArray[i].isDone || (processArray[i].iArrivalTime > tickedTime))
                continue;

            int remainingTime = processArray[i].iBurstTime - processArray[i].iTickTime;
           // cout << "Remaining time of #" << i << " : " << remainingTime << endl;

            roundTime = remainingTime > m_uiTimeQuantum ? m_uiTimeQuantum:remainingTime;
           // cout << "Round time for #" << i << " : " << roundTime << endl;
            processArray[i].iTickTime += roundTime;


            for(int j = 0; j < m_uiProcessCount; j++)
            {
                if(i == j || processArray[j].isDone || (processArray[j].iArrivalTime > tickedTime))
                    continue;

                processArray[j].iWaitingTime += roundTime;
                avg += roundTime;
            }

            if (processArray[i].iBurstTime == processArray[i].iTickTime)
            {
               // cout << "Process #" << i << " done." << endl;
                processArray[i].isDone = true;
            }

            tickedTime += roundTime;
         }
    }
    while(tickedTime < totalTick);


    //cout << "Ticked time : " << tickedTime << "Total tick : " << totalTick << endl;
    return avg / m_uiProcessCount;
}


Evet, bir makalenin daha sonuna geldik. İlerleyen zamanlarda ara ara eski ödev ve projelere yer vermeye devam edeceğim. Sağlıcakla kalın!

Monday, January 12, 2015

[Java] - Abstract(soyut) sınıflar ve sınıflar arası kalıtım

Evet, başlıktan da anlayabileceğiniz üzere bugünkü konumuz object oriented programming'in en önemli konularından birisi olan soyut sınıflar ve kalıtım. İsterseniz önce bu kavramlara daha genel bakarak ne olduklarını anlayalım.

Kalıtım denince aklınıza ilk önce biyoloji gelir. Kalıtım, herhangi bir canlının kendi türündeki bir sonraki nesile aktardığı özelliklerin genelidir. Obje-tabanlı programlamada da mantık bu şekilde işler. Bunu basit bir örnekle açıklayalım.

İlk sınıfımız öğrenci sınıfı olsun;


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Student
{
 private int Age;
 private int Weight;
 private int Height;
 
 private int AverageGrade;
 private int Level;
 
 
 public int getAge(){return Age;}
 public int getWeight(){return Weight;}
 public int getHeight(){return Height;}
 public int getAvgGrade(){return AverageGrade;}
 public int getLevel(){return Level;}
 
 Student(int a, int w, int h) 
 {
  Age = a;
  Weight = w;
  Height = h;
 }

}
İkinci sınıfımız ise Asker sınıfı olsun;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Soldier 
{
 private int Age;
 private int Weight;
 private int Height;
 
 private int Rank;
 private int Badges;
 
 
 public int getAge(){return Age;}
 public int getWeight(){return Weight;}
 public int getHeight(){return Height;}
 public int getRank(){return Rank;}
 public int getBadges(){return Badges;}
 
 Soldier(int a, int w, int h) 
 {
  Age = a;
  Weight = w;
  Height = h;
 }
}

Farkettiğiniz üzere, bu iki sınıfın paylaştığı ortak değerler bulunmakta. Eğer biz bu iki sınıfı ortak bir çatı altında toplamak isteseydik, toplayabileceğimiz çatı muhtemelen 'kişi' olurdu. Ve bu kişi sınıfı, hem öğrenci hem askerdeki Yaş,Kilo ve Boy değişkenlerine sahip olurdu. Kalıtımın devreye girdiği nokta işte tam burası. Eğer ortak değişkenlere, fonksiyonlara sahip birden fazla sınıf yaratacaksak, kalıtım işimizi aşırı derecede kolaylaştırıyor. Şimdi kalıtımda inherit edilecek, yani özellikleri alt sınıflara aktarılacak sınıfı nasıl yaratacağımızı görelim.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Person 
{

 private int Age;
 private int Weight;
 private int Height;
 
 
 public int getAge(){return Age;}
 public int getWeight(){return Weight;}
 public int getHeight(){return Height;}
 
 Person(int a,int w, int h)
 {
  Age = a;
  Weight = w;
  Height = h;
 }
 
}

Üst sınıfımız, yani kalıtımın kaynağı olan sınıfa gördüğünüz gibi, alt sınıflarda paylaşılacak olan bütün değişkenleri ve fonksiyonları ekledik. Dikkat ettiyseniz Person sınıfı Soldier sınıfındaki Badges ve Rank değişkenlerini ve Student sınıfındaki AverageGrade ve Level değişkenlerini içermiyor. Bunun sebebi bu değişkenlerin tanımlandığı sınıfa özel değişken olmaları. Öğrenciler rütbeye ve madalyaya sahip olmazken, askerler de ortalama not ve sınıfa sahip değiller. Dolayısıyla bunlar genel çatıya dahil değil.

Şimdi yarattığımız Person sınıfının özelliklerini kalıtımla alt sınıflara nasıl aktaracağımızı görelim.

 Student sınıfı ;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class Student extends Person
{
 private int AverageGrade;
 private int Level;
 
 public int getAvgGrade(){return AverageGrade;}
 public int getLevel(){return Level;}
 
 Student(int a, int w, int h) 
 {
  super(a,w,h);
 }
}

Soldier sınıfı;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class Soldier extends Person
{
 private int Rank;
 private int Badges;
 
 public int getRank(){return Rank;}
 public int getBadges(){return Badges;}
 
 Soldier(int a, int w, int h) 
 {
  super(a, w, h);
 }
}

Öncelikle gözünüze çarpan ilk değişiklik muhtemelen her iki sınıftaki Age,Weight ve Height değişkenlerinin ve bunların getter methodlarının artık bulunmadığıdır. Ve sınıfımızın tanımlamasının yanına gelen 'extends' kelimesi de bizim için yeni bir olgu.

Herhangi bir sınıf adının yanına gelen extends kelimesi, herhangi bir özelliği kalıtımla üstten alacağı sınıfı belirtmek için kullanılır, yani 'public class Soldier extends Person' satırının Türkçe karşılığı, 'Asker sınıfı Person sınıfının bütün özelliklerini kalıtımla alır' olacaktır. Buradaki ilişkiyi baba-oğul ilişkisi gibi düşünebilirsiniz.

Aklınıza takılabilecek sorulardan bazılarını cevaplayayım. Mesela, 'Ben Soldier sınıfı için getAge() fonksiyonunu kullanabilir miyim?' gibi bir soru soracak olursanız, cevabı evet. Üst sınıftan inherit edilen bütün değişkenler ve bütün fonksiyonlar, alt sınıfta sanki o sınıfta tanımlanmış gibi kullanılabilir.

Gelelim constructor içerisindeki super(a,w,h); olayına. 'super' keywordü, üst sınıftaki herhangi bir fonksiyonu alt sınıf içerisinden çağırmak için kullanılır, Örnek vermek gerekirse, Soldier sınıfının constructoru içerisindeki super(a,w,h); Person sınıfının constructorunu çağırıyor. Constructor harici diğer fonksiyon ve değişkenlere erişmek içinse super. kullanılır.

Şimdi, işleri biraz daha karmaşıklaştıralım ve 'soyut' aleme geçelim :)

Abstract sınıflar, yani soyut sınıflar genellikle alt sınıflar için taslak teşkil ederler. Abstract sınıflar soyut tanımlamalar(fonksiyon vs.) içerebilirler, veya içermeyebilirler. Abstract sınıflarda soyut tanımlanan fonksiyonların gövdeleri yoktur ve inherit eden alt sınıflar bu fonksiyonları override(yeniden tanımlama) etmek zorundadırlar. Abstract sınıfların en önemli özelliği ise, kendi başlarına bir objeye atanamazlar. Yani AbstractClass ac = new AbstractClass(); tanımlaması yanlıştır. Abstract sınıflar sadece inherit edilebilirler.

Dip not : Eğer abstract sınıfı bir başka abstract sınıf inherit ediyorsa, inherit eden sınıf üst sınıftaki abstract methodları tanımlamak zorunda değildir.

Basit bir örnekle bu konuyu biraz daha aydınlatalım.

Eşkenar üçgen sınıfı;


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Eşkenar üçgen sınıfı
public class EquilateralTriangle 
{
 // Kenar uzunluğu
 private int a;
 
 EquilateralTriangle(int a)
 {
  this.a = a;
 }
 
 // Alan hesabı
 public double calculateArea()
 {
  return 3*a;
 }
 
 // Çevre hesabı
 public double calculatePerimeter()
 {
  // a kare çarpı kök üç bölü dört..
  return ((a*a) * Math.sqrt((double)3)) / 4;
 }
}

İkizkenar üçgen sınıfı;


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// İkizkenar üçgen
public class IsoscelesTriangle 
{
 private int a;
 private int height;
 private int floor;
 
 IsoscelesTriangle(int a,int height,int t)
 {
  this.a = a;
  this.height = a;
  this.floor = t;
 }
 
 
 // Alan hesabı
 public double calculateArea()
 {
  return (floor * height) / 2;
 }
 // Çevre hesabı
 public double calculatePerimeter()
 {
  return Math.pow(a, 2) + floor;
 }
}


Farkettiğiniz üzere bu iki sınıfta aynı adda iki fonksiyon var, fakat gövdeleri farklı. Çünkü, her üçgen bir alana ve çevreye sahiptir fakat farklı üçgenlerin alan ve çevre hesaplama formülleri farklıdır. Bu durumda soyut sınıflardan yararlanabiliriz. Yaratacağımız yeni soyut sınıf ise tabiki 'Triangle' sınıfı olacak.


(Abstract) Triangle sınıfımız;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public abstract class Triangle 
{
 // Her iki sınıfta da ortak, kenar uzunluğu
 protected int a;
 // Her iki sınıfta da ortak, yükseklik
 protected int h;
 
 Triangle(int a, int h)
 {
  this.a = a; 
  this.h = h;
 }
 public abstract double calculateArea();
 public abstract double calculatePerimeter();
}

Alt sınıfları tanımlamaya başlamadan önce burada birkaç şeye değinmek istiyorum. class kelimesinden önce kullandığımız 'abstract' keywordu tahmin edebileceğiniz gibi sınıfın soyut olduğunu belirtiyor. Abstract sınıfımızın içerisinde diğer sınıflarda olduğu gibi değişkenler bulunabilir.

En altta tanımlanan 2 soyut method ise, inherit edecek sınıflarda bulunan ortak fakat gövdeleri farklı olan methodlar. Şimdi yukarıdaki Eşkenar ve İkizkenar üçgen sınıflarını soyut sınıfı inherit ederek tekrardan tanımlayalım.


Eşkenar üçgen;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Eşkenar üçgen sınıfı
public class EquilateralTriangle extends Triangle
{
 EquilateralTriangle(int a,int h)
 {
  super(a,h);
 }
 
 // Alan hesabı
 public double calculateArea()
 {
  return 3*a;
 }
 
 // Çevre hesabı
 public double calculatePerimeter()
 {
  // a kare çarpı kök üç bölü dört..
  return ((a*a) * Math.sqrt((double)3)) / 4;
 }
}
İkizkenar üçgen;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// İkizkenar üçgen
public class IsoscelesTriangle extends Triangle
{
 // Her iki sınıfta da ortak, kenar uzunluğu
 private int floor;
 IsoscelesTriangle(int a,int height,int t)
 {
  super(a,height);
  this.floor = t;
 }
 
 // Alan hesabı
 public double calculateArea()
 {
  return (floor * h) / 2;
 }
 // Çevre hesabı
 public double calculatePerimeter()
 {
  return Math.pow(a, 2) + floor;
 }
}

Yaptığımız tek şey daha önceki inheritance örneklerinde olduğu gibi extends keywordunu kullanarak Eşkenar ve İkizkenar üçgen sınıflarına Triangle sınıfını inherit etmek oldu.

Önemli noktalar;

 - Abstract tanımlı bir methodu, eğer inherit eden sınıf abstract değilse, sınıfta mutlaka implement etmeli, yani gövdeye kavuşturmalısınız. Aksi taktirde programınız derlenmeyecektir.

 - Üst sınıftaki değişkenlerin alt sınıflarda görünür olmasını istiyorsanız, protected veya public modifierlerini kullanmalısınız. 


Evet, bir makalenin daha sonuna gelmekle beraber, yarınki OOP finaline girecek olan arkadaşlarıma başarılar dilerim.

Sağlıcakla kalın :)

Monday, January 5, 2015

[C++] İş parçacıkları(Thread)

Evet, bugünkü konumuz iş parçacıkları, yani Thread'lar. Öncelikle bir iş parçacığının ne olduğu ile başlayalım.

İş parçacıkları, bir işlemin(process) sahip olduğu, belirli işler ve fonksiyonlar atayabildiği ayrı bir işlem gibi düşünülebilir. Varsayılan olarak her işlem "bir" iş parçacığına sahiptir, ve bu iş parçacığına "Ana iş parçacığı" denir(Main thread). Sadece bir iş parçacığına sahip uygulamalara ise tek-iş parçacıklı, yani single threaded uygulamalar adı verilir.

Normal olarak işletim sisteminin zamanlama algoritmasında, çalışmakta olan her işleme işlemciyi kullanması için bir zaman periyodu tanınır. Bu zaman periyodunda, işlem en son kaldığı yerden devam ederek bir sonraki fonksiyonu veya komutu işler, zamanı bittiğinde ise en son durumu işlem bloğuna kaydedilerek bir sonraki işleme geçilir. Örnek olarak;

[ explorer.exe | chrome.exe | csrss.exe | svchost.exe | cmd.exe | calc.exe | steam.exe | .... | winlogon.exe ]
0              5            10          17            24        30         32          45                    150

Çok-iş parçacıklı uygulamalarda ise, işlemin gerçekleştirdiği belirli fonksiyonlara bir iş parçacığı atanmıştır ve bu iş parçacıkları zamanlayıcı(scheduler) tarafından bağımsız olarak işlenirler. Örnek olarak;

[ explorer.exe |  chrome.exe  | ...]
[   t1|t2|t3   |  t1|t2|t3|t4 | ... ]
0     2  3     5    7  8  9   11


 Farzedelim ki, bir konsol uygulamamız var ve uygulamamızdaki bir hesaplama fonksiyonunun tamamlanma süresi 10 saniye olsun. Eğer uygulamanız tek iş parçacığına sahipse, uygulamanız 10 saniyelik fonksiyonunuzu çağırdığında donma yaşar ve bu süre zarfında ekrana yazacağınız herhangi bir komutu algılamaz. Örnek teşkil etmesi açısından aşağıdaki kodu deneyin ve sonucu görün. (bilindik bir örnek olarak pi sayısı hesaplama algoritması vereceğim)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include "stdafx.h"
#include <cmath>
#include <iostream>

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
 double sum = 0;
 double k;

 cout << "Pi sayisi hesaplaniyor.." << endl;
 for (k = 0; k <= 100000000; k++)
  sum += (sqrt(12)*(pow(-1, k) / ((2 * k + 1)*pow(3, k))));

 cout << "pi sayisi : " << sum << endl;
 cin.ignore();
 return 0;
}

Farkettiğiniz üzere, pi sayısı hesaplanırken konsola yazdığınız karakterler görünmezken, pi sayısı hesaplandıktan sonra yazdığınız tüm karakterler konsol ekranında belirdi. Bunun sebebi, çalışan tek iş parçacığınızın, yani ana iş parçacığının o an pi sayısını hesaplamakla uğraşıyor olmasıydı. Dolayısıyla klavyenizden gelen keystroke olaylarını işleyen fonksiyona vakit kalmadı.



Hepinizin kafasında beliren soru işaretini az çok tahmin edebiliyorum, "peki bu durumun üstesinden nasıl gelebiliriz?". Cevabı yeni iş parçacıkları yaratmakta yatıyor. İsterseniz ufaktan başlayalım.

Öncelikle, iş parçacıklarını Windows platrormunda kullanacaksanız, programınızda <Windows.h> kütüphanesini include etmelisiniz. Daha sonra yapmanız gereken şey ise, iş parçacığınız için bir fonksiyon yazmak olacak. Konunun devamını aşağıdaki kod üzerinde anlatacağım;




  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
// ThreadExample.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <iostream>

using namespace std;


// Global thread handle değişkeni
HANDLE thrHandle = NULL;

// İş parçacığının sonlanıp sonlanmaması gerektiğini tutan koşul değişkeni
bool m_bRunning = true;

DWORD WINAPI ThreadCall(LPVOID lpParam)
{
 // Durmalı mıyız?..
 while (m_bRunning)
 {
  cout << "is parcacigi says : i've got lots of work to do.." << endl;
  // İş parçacığımızı uyutalım..
  Sleep(1000);
 }
 cout << "is parcacigi durdu.." << endl;
 // İş parçacığımız başarı ile tamamlandı, geriye STATUS_SUCCESS döndürelim
 return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
 // Konsoldan okunan tuş verisini tutacak değişkenimiz
 char c;
 cout << "is parcacigi yaratiliyor.." << endl;
 /*
  CreateThread fonksiyonu, Windows platformunda iş parçacığı yaratmak için kullanılır.

  

  Aldığı 6 parametre bulunmaktadır, geriye yaratılan iş parçacığının unique handle değerini döndürür.
  Bu handle değeri iş parçacığını kontrol edebilmemiz için gerekli olduğundan, thrHandle değişkenine atadık.

  Parametrelerde şuan için ilgilenmeniz gereken sadece üçüncü ve beşinci parametre olduğu için,
  diğerlerini daha sonraki bir makalede anlatacağım.

  Üçüncü parametre, iş parçacığının fonksiyonudur. Yani iş parçacığı başladığında
  hangi fonksiyonu işletecek, onu belirtiyoruz.

  Beşinci parametre ise iş parçacığının nasıl yaratılacağını belirtir, 

  0 = iş parçacığı yaratılır yaratılmaz başlar
  CREATE_SUSPENDED(0x00000004) = iş parçacığı duraklatılmış durumda yaratılır (daha sonra ResumeThread ile başlatılması gerekir)

  Bu örneğimizde iş parçacığını duraklatılmış olarak yaratıp, sonra ResumeThread fonksiyonu ile başlatacağız.
 */
 thrHandle = CreateThread(NULL, 0, ThreadCall, NULL, CREATE_SUSPENDED, NULL);

 // thrHandle değeri sıfırdan büyükse, iş parçacığımız başarı ile yaratılmış demektir.
 if (thrHandle)
 {
  cout << "is parcacigi yaratildi, is parcacigi handle : " << thrHandle << endl;
 }
 cout << "is parcacigi baslatiliyor.." << endl;
 /*

  ResumeThread fonksiyonu, duraklatılmış herhangi bir iş parçacığını devam ettirmek 
  için kullanılır.
  Parametre olarak sadece duraklatmak istenilen iş parçacığının handle
  değerini alır.
 
 */
 ResumeThread(thrHandle);
 cout << "is parcacigi baslatildi.." << endl;
 cout << "is parcacigini duraklatmak icin 's',devam ettirmek için 'r', sonlandırmak için 'e' yazin.." << endl;
 while (true)
 {
  cin >> c;
  switch (c)
  {
  case 'e':
   m_bRunning = false;
   break;
  case 's':
   /*
    SuspendThread fonksiyonu çalışmakta olan herhangi bir iş parçacığını duraklatmak amacı
    ile kullanılır. Parametre olarak sadece duraklatmak istenilen iş parçacığının handle 
    değerini alır.
   
   */
   SuspendThread(thrHandle);
   cout << "is parcacigi duraklatildi. devam ettirmek icin 'r' yazin." << endl;
   break;
  case 'r':
   ResumeThread(thrHandle);
   cout << "is parcacigi devam ettirildi. duraklatmak için 's' yazin." << endl;
   break;
  }
 }
 
 cin.ignore();
 return 0;
}

Yukarıdaki uygulamayı çalıştırdığımızda, arka planda çalışan ThreadCall fonksiyonunun ana iş parçacığını bloke etmediğini farkedeceksiniz. Basit olarak multithreading mimarisi bunun üzerine kuruludur. Günümüzde neredeyse bütün uygulamalar multithreading kullanılarak yazılmaktadır.

Multithreading kullanmanın, özellikle çok-çekirdekli işlemcilerde gözle görünür performans artışı sağladığını göreceksiniz.

Bugün işlemin ana iş parçacığından bağımsız bir iş parçacığının nasıl yaratılacağını ve bu iş parçacığını nasıl kontrol edebileceğinizi öğrendiniz. İlerleyen zamanlarda fırsatını bulduğunuzda uygulamalarınızda bu methodu kullanarak gözle görünür düzeyde performans artışı ve stabilite sağlayabilirsiniz.

Umarım faydalı bir makale olmuştur.

İlk verdiğimiz pi sayısı örneğinin multithreading kullanılarak çözülmüş hali;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// ThreadExample.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <iostream>

using namespace std;


// Global thread handle değişkeni
HANDLE thrHandle = NULL;

// İş parçacığının sonlanıp sonlanmaması gerektiğini tutan koşul değişkeni
bool m_bRunning = true;

DWORD WINAPI ThreadCall(LPVOID lpParam)
{
 double sum = 0;
 // Durmalı mıyız?..
 while (m_bRunning)
 {
  sum = 0;
  double k;
  for (k = 0; k <= 1000000; k++)
   sum += (sqrt(12)*(pow(-1, k) / ((2 * k + 1)*pow(3, k)))); 
 }
 cout << "pi sayisi : " << sum << endl;
 cout << "is parcacigi durdu.." << endl;
 return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{

 char c;
 cout << "is parcacigi yaratiliyor.." << endl;

 thrHandle = CreateThread(NULL, 0, ThreadCall, NULL, CREATE_SUSPENDED, NULL);

 if (thrHandle)
 {
  cout << "is parcacigi yaratildi, is parcacigi handle : " << thrHandle << endl;
 }
 cout << "is parcacigi baslatiliyor.." << endl;

 ResumeThread(thrHandle);
 cout << "is parcacigi baslatildi.." << endl;
 cout << "is parcacigini duraklatmak icin 's',devam ettirmek için 'r', sonlandırmak için 'e' yazin.." << endl;
 while (true)
 {
  cin >> c;
  switch (c)
  {
  case 'e':
   m_bRunning = false;
   break;
  case 's':
  
   SuspendThread(thrHandle);
   cout << "is parcacigi duraklatildi. devam ettirmek icin 'r' yazin." << endl;
   break;
  case 'r':
   ResumeThread(thrHandle);
   cout << "is parcacigi devam ettirildi. duraklatmak için 's' yazin." << endl;
   break;
  }
 }
 
 cin.ignore();
 return 0;
}



Dip not : İlerleyen günlerde aynı konuyu C# ile de işlemeyi planlıyorum, .NET platformu kullananlar üzülmesinler :)

Gelecek konular : İş parçacıkları arası senkronizasyon, 'deadlock' nedir, senkronizasyon bileşenleri(mutex, critical section, semaphore) ve daha fazlası.

Sağlıcakla kalın.