C Üzerinde Asenkron Giriş Çıkış İşlemi

Nedir?

Asenkron giris cikis islemleri, ReadFile ve WriteFile fonksiyonlarini kullandigimizda, bu fonksiyonlarin blokeye sebep olmadan calismalarini saglamaktir.(en azindan biz su anda bunu gorucez:-) )
ReadFile ve WriteFile atomik fonksiyonlardir ve okuma ve yazma islemi tamamlanmadan geri donmezler. Bu sirada( yani WriteFile ve ReadFile calisirken ), bu fonksiyonlardan herhangi birini cagiran thread, bu islem kernel tarafindan  gerceklenene kadar cizelge disi bekletilir ve islem bittiginde fonksiyon geri donerek, thread’i tekrar cizelgeye sokar.

Bu tabloya bakildiginda eger bir arkaplan(asenkron, cakisik[overlapped],, adi herneyse artik) yazma ve okuma yapmak istersek, kendimize bunun icin yeni bir thread olusturmali ve bu fonksiyonlari onun icerisinde cagirmaliyiz.

Bu fonksiyonlarin kendi iclerinde bu problemi cozdukleri hakkinda dokumanlar microsoftun sitesinde mevcut fakat hic denemedim. Biz burda kendi cozumumuzu yapicaz. ;-)

Neden ihtiyac duyabiliriz ?

En basitinden buyuk bir dosya okuyucaz veya yazicaz diye dusunelim ve bu sirada da programimizin baska bir islem yapmasini istiyoruz(yada donmamasini). Iste bunu saglmak icin asenkron islemleri kullanmaliyiz.

Bir ornek

Aslinda bu ornegi pencereli yapmamiz konuyu daha anlasilir hale getirirdi fakat w32’de yazilan programlarin uzunlugundan dolayi sitede paylasamiyorum. Daha sonra Alakali bir ornek yapip proje dosyasini koyabilirim.

Ornegimiz; buyuk bir dosya okumasi yapan konsol programina iliskin olsun. Bu program o buyuk dosyayi okurken, ayni zamanda, kendi capinda bir progressbar gostersin. Progressbar dedigime bakmayin ‘Dosya okunuyor...’ yazisinin noktalarini artirip azalticaz, pek bi numarasi yok yani :-)

#include <Windows.h>
#include <stdio.h>
#include <tchar.h>


#define STILLWAITING 2
#define BYTECOUNT 500 * 1024 * 1024


/********************************************************
 * BeginReadFile icerisinden cagrilan thread'e,	    *
 * ihtiyaci olan bilgileri gondermek icin gereken yapi   *
 ********************************************************/
struct st_file
{
	HANDLE hFile;
	LPVOID buff;
	DWORD dwCount;
	DWORD dwctRead;
};

/********************************************************
 * BeginReadFile'a ihtiyaci olan bilgileri ve okuma	    *
 * islemi bittikten sora cagrilicak fonksiyonun adresini*
 * gondermek icin gerekli yapi.		  		    *
 * (Thread'e parametre olarak giden yapi)		    *
 ********************************************************/
struct st_cb_file
{
	struct st_file *pst;
	void (*cb_complete)(struct st_file *);
};

/********************************************************
 * Asenkron okuma islemini baslatan fonksiyon		    *
 ********************************************************/
HANDLE CALLBACK 
	BeginReadFile(HANDLE hFile, LPVOID lpBuffer,
				  DWORD dwOffset, void (*cb_complete)(struct st_file *));


/********************************************************
 * Asenkron okuma islemini bitiren fonksiyon		    *
 ********************************************************/
DWORD CALLBACK EndReadFile(HANDLE hRead, DWORD dwMilliseconds);

/********************************************************
 * Dosyayi okuyacak thread'in fonksiyonu		    *
 ********************************************************/
DWORD CALLBACK thr_read_file(LPVOID lp);


void read_complete(struct st_file *pst)
{
	printf(	"\nHFile: 0x%p\n"\
			"dwCount: 0x%X\n"\
			"dwctRead: 0x%X\n"\
			"buff: 0x%p\n", pst->hFile, pst->dwCount, pst->dwctRead, pst->buff);
}


int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hFile, hRead, hStdout;
	DWORD dwCount, dwrtRead;
	void *buff;
	int i;
	COORD cr;

	if ((buff = malloc(BYTECOUNT)) == NULL)
	{
		printf("hafiza yetersiz.\n");
		return 0;
	}

	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

	hFile = CreateFile(
		_T("testfile"),
		GENERIC_READ,				/*dosya haklari(okuma)*/
		FILE_SHARE_READ,			/*dosya paylasimi(okuma)*/
		NULL,					/*guvenlik nitelikleri*/
		OPEN_EXISTING,			/*yaratim niteligi*/
		0,					/*dosya niteligi*/
		NULL);					/*sablon dosya*/

	if (hFile == INVALID_HANDLE_VALUE || hStdout == NULL)
	{
		printf("Hata: dosya acilamiyor.\n");
		goto EXIT;
	}

	if ((hRead = BeginReadFile(hFile, buff, BYTECOUNT, read_complete)) == NULL)
	{
		printf("Hata: okuma baslatilamadi.\n");
		goto EXIT;
	}

	printf("Dosya Okunuyor");

	cr.Y = 0;
	cr.X = 14;/*dosya okunuyor kelimesi 14 karakter ;-)*/

for (; ; ){
	for (i = 0; i < 5; ++i)
	{
		if (/*50 milisaniye bekle.bitmisse ? zipla*/
			(dwrtRead = EndReadFile(hRead, 50)) != STILLWAITING
			)
			goto BREAKPTLOOPS;

		putchar('.');
		cr.X++;
	}

	for (; i > 0; --i)
	{
		if (/*50 milisaniye bekle.bitmisse ? zipla*/
			(dwrtRead = EndReadFile(hRead, 50)) != STILLWAITING
			)
			goto BREAKPTLOOPS;

		cr.X--;
		SetConsoleCursorPosition(hStdout, cr);
		putchar(' ');
	}

	SetConsoleCursorPosition(hStdout, cr);

}

BREAKPTLOOPS:

	printf(!dwrtRead ? "\nOkunamadi\n" : "\nOkundu\n");

EXIT:
	getchar();
	free(buff);
	return 0;
}


DWORD CALLBACK thr_read_file(LPVOID lp)
{
	int retVal;
	struct st_cb_file *pstCb;
	struct st_file *pst;

	pstCb = (struct st_cb_file *)lp;
	pst = pstCb->pst;

	if (!ReadFile(pst->hFile, pst->buff, pst->dwCount, &pst->dwctRead, NULL))
	{
		free(pst);
		free(pstCb);
		return FALSE;
	}

	if (pstCb->cb_complete != NULL)
		pstCb->cb_complete(pst);

	free(pst);
	free(pstCb);

	return TRUE;
}

HANDLE CALLBACK BeginReadFile(
	HANDLE hFile, LPVOID lpBuffer,
	DWORD dwCount, void (*cb_complete)(struct st_file *))
{
	DWORD dwThrID;
	struct st_file *pst;
	struct st_cb_file *pstCb;

	pst = (struct st_file *)malloc(sizeof(struct st_file));
	pstCb = (struct st_cb_file *)malloc(sizeof(struct st_cb_file));

	pst->hFile = hFile;
	pst->dwCount = dwCount;
	pst->buff = lpBuffer;

	pstCb->cb_complete = cb_complete;
	pstCb->pst = pst;

	HANDLE hThr = CreateThread(NULL, 0, thr_read_file, pstCb, 0, &dwThrID);

	if (hThr == NULL)
	{
		free(pst);
		free(pstCb);
		return NULL;
	}

	return hThr;
}


DWORD CALLBACK EndReadFile(HANDLE hRead, DWORD dwMilliseconds)
{
	DWORD dwExitCode;

	if (WaitForSingleObject(hRead, dwMilliseconds) != WAIT_OBJECT_0)
		return STILLWAITING;

	if (GetExitCodeThread(hRead, &dwExitCode))
		return dwExitCode;

	return 0;
}

BeginReadFile

Bu fonksiyon bir thread yaratir ve bu thread’e(thread fonksiyonuna) parametre olarak, okunucak dosyanin handle’ini, okuma islemi bittikten sonra cagrilicak fonksiyonu ve ek olarak gerekli bir takim bilgileri gonderir. Yarattigi thread’in handle degeriyle geri doner. Boylece blokeye sebep olmamis olur. Bu fonksiyonun cagrildigi akis(thread) bu fonksiyondan geri donerken, diger yandan bu fonksiyonun cagirdigi thread dosyayi okur.

EndReadFile

Bu fonksiyon BeginReadFile fonksiyonundan donen handle degerini ve bekleme suresini parametre olarak alir. WaitForSingleObject ile parametre olarak aldigimiz sure kadar blokede kalarak, thread’imizin(yaniBeginReadFile’dan donen handle’in)bitip bitmedigini arastirir. Eger bitmediyse STILLWAITING sabitiyle geri doner. Eger bittiyse thread’imizin geri donus degeri alinir ve onunla geri donulur. E oda olmazsa zaten hata vardir. Geriye 0 doner.

thr_read_file

Bu fonksiyon, BeginReadFile fonksiyonu icinde olusturulan thread’in fonksiyonudur ve gercekten okumanin yapildigi yer bu fonksiyon icindedir.
Hayirli olsun !! Bu kadar :-)

 

Tarih:
Hit: 2809
Yazar: guru



Yorumlar


Siftahı yapan siz olun
Yorum yapabilmek için üye girişi yapmalısınız.