Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Низкоуровневое программирование > Win Api
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 27.11.2011, 18:17   #1
RAZOR1703
Пользователь
 
Регистрация: 15.12.2010
Сообщений: 74
По умолчанию Обмен данными между процессами. Но вопрос по семафоры :)

Всем привет. Такое дело. Нужно было написать программу, а точнее две программы, которые реализуют следующую вещь:
Первая - сервер - содержит две нити (или потока, как вам угодно) - производителя. Они должны поочередно передавать информацию во вторую программу.
Вторая - клиент - содержит лишь функцию получения данных от нитей сервера.
Одновременно доступ к файлу, отображаемому в память, может иметь только кто-то один: либо одна из нитей, либо функция чтения. В задании сказано сделать это с семафорами. Сделать-то я сделал, но, поскольку не совсем дружу с семафорами и на событиях было бы куда проще это сделать, все получилось через одно место.
А проблема, собственно, в том, что пишет только одна из нитей, и в чем дело я понять не могу. Есть еще одна маленькая неприятность: из-за дебильно повешенного семафора процесс-клиент читает пустую информацию из файла, пока не начинает работать нить. Но первостепенной, все же, является первая проблема.
Код:
Сервер:
Код:
#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <conio.h>

#define PRODUCER_NUM 1
	HANDLE hSem, hBufSem;
	HANDLE hMFile;
	HANDLE hProduserThread[2];
	unsigned long uThreadID[2];
	char Sem[] = "MySem";
	char BufSem[] = "MyBufSem";
	char Fn1[] = "MyMemoryMappedFile";
	LPVOID StartMFile;
	char Buf[100];

void produser(LPVOID lpParam)
{
	int num = (int) lpParam;
	srand( time(0) );

	do {
		WaitForSingleObject (hSem, INFINITE);
		if ((WaitForSingleObject (hBufSem, 10) == WAIT_TIMEOUT))
		{	ReleaseSemaphore (hSem, 1, 0);
			break;
		}
		sprintf (Buf, "Producer №%d produces: %d", num, rand()%2000);
		printf ("%s\n", Buf);
		CopyMemory (StartMFile, Buf, sizeof(Buf) );
		ReleaseSemaphore (hSem, 1, 0);
		Sleep (rand()%20);
		}
	while (1);
}

int main ()
{
	hSem = OpenSemaphore (SEMAPHORE_ALL_ACCESS, TRUE, Sem);
	if (!hSem) 
	{
		hSem = CreateSemaphore(NULL, 1, 1, Sem);

		if (!hSem)
		{
			printf ("Error while creating semaphore\n");
			return 0;
		}
	}
	
	else
	{
		CloseHandle (hSem);
		return 0;
	}
	
	hBufSem = CreateSemaphore (NULL, 1, 1, BufSem);
	hMFile = CreateFileMapping ( (HANDLE) 0xFFFFFFFF, NULL, PAGE_READWRITE, 0, 100, Fn1);
	
	if (!hMFile)
	{
		printf ("Error while creating file\n");
		return 0;
	}
	
	StartMFile = MapViewOfFile (hMFile, FILE_MAP_WRITE, 0, 0, 100);
	
	if (!StartMFile)
	{
		printf ("Error while creating map view of file\n");
		return 0;
	}
	
	_getch(); //Чтобы дать время запустить клиент
	printf ("Producers have started their work\n");
	for (int i = 0; i < 2; i++)
		hProduserThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)produser, (LPVOID)i, 0, &uThreadID[i]);

	_getch();
	
	for (int i = 0; i < 2; i++ )
        CloseHandle (hProduserThread);
	UnmapViewOfFile (StartMFile);
	CloseHandle (hMFile);
	return 0;
}
Клиент:
Код:
#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <conio.h>


	HANDLE hSem, hBufSem;
	HANDLE hMFile;
	char Sem[] = "MySem";
	char BufSem[] = "MyBufSem";
	char Fn1[] = "MyMemoryMappedFile";
	LPVOID StartMFile;
	char Buf[100];


int main()
{
	hSem = OpenSemaphore(SEMAPHORE_ALL_ACCESS, 1, Sem);
	if (!hSem) {
		printf("Errror while opening semaphore\n");
		return 0;
	}

	hBufSem = OpenSemaphore(SEMAPHORE_ALL_ACCESS, 1, BufSem);
	if (!hBufSem) {
		printf("Errror while opening semaphore\n");
		return 0;
	}

	hMFile = OpenFileMapping( FILE_MAP_READ, TRUE, Fn1);
	if (!hMFile)
	{
		printf("Error while opening file mapping\n");
		return 0;
	}
	StartMFile = MapViewOfFile (hMFile, FILE_MAP_READ,0, 0,100);
	if (!StartMFile)
	{
		printf("Error\n");
		return 0;
	}
	CloseHandle(hMFile);
	if (!StartMFile)
	{
		printf("Error\n");
		return 0;
	}
	printf("CLIent started\n");
	do
	{
		WaitForSingleObject(hSem, INFINITE);
		CopyMemory (Buf,StartMFile, 100);
		printf("%s\n",Buf);
		ReleaseSemaphore (hBufSem, 1, 0);
		ReleaseSemaphore(hSem, 1, 0);
	}
	while (1);
	UnmapViewOfFile(StartMFile);
	return 0;
}
Сначала запускаем сервер, потом клиент, а потом жмем какую-нибудь клавишу в консоли сервера.
В общем, кому не сложно, помогите грамотно повесить семафоры на всё это дело.
RAZOR1703 вне форума Ответить с цитированием
Старый 27.11.2011, 23:15   #2
rpy3uH
добрый няша
Старожил
 
Аватар для rpy3uH
 
Регистрация: 29.10.2006
Сообщений: 4,804
По умолчанию

Во-первых, зачем два семафора? одним спокойно можно обойтись
Во-вторых
Код:
WaitForSingleObject(hSem, INFINITE);
CopyMemory (Buf,StartMFile, 100);
printf("%s\n",Buf);
ReleaseSemaphore (hBufSem, 1, 0);//<---- а где захват hBufSem?
ReleaseSemaphore(hSem, 1, 0);
в-третьих, использовать семафоры надо только когда действительно нужно. в 95% случаев можно обойтись мьютексами. Мьютекс это частный случай семафора, мьютекс это тотже семафор, только счётчик-инициализатор у него всегда равен 1.

В-четвёртых, зачем это?
Код:
if ((WaitForSingleObject (hBufSem, 10) == WAIT_TIMEOUT))
{	ReleaseSemaphore (hSem, 1, 0);
	break;
}

Последний раз редактировалось rpy3uH; 27.11.2011 в 23:24.
rpy3uH вне форума Ответить с цитированием
Старый 27.11.2011, 23:58   #3
RAZOR1703
Пользователь
 
Регистрация: 15.12.2010
Сообщений: 74
По умолчанию

Я в курсе, что можно одним. Если я делаю одним, то пишется-то с двух нитей, но каким-то образом читается только с одной и нет гарантии, что несколько раз подряд не пройдет запись. Мне нужно что-то вроде "записал-считал-записал", то есть "записал-записал-считал" недопустимо.
Отвечу сразу на первое и на четвертое. Захват hBufSem происходит в нитях в сервере. Этот семафор сигнализирует о том, что якобы буфер пуст и можно писать.
Конструкция "в-четвертых" нужна затем, чтобы отслеживать, считан ли был буфер. Если этот семафор не освободится за 10 мс, то эта нить пролетела, т.к. буфер был по-прежнему несчитан.
Цитата:
использовать семафоры надо только когда действительно нужно
Согласен. Но увы, такое задание.

Если вас не затруднит, можете привести это к человеческому виду? Буду очень благодарен, так как все вечера со среды и весь сегодняшний день потрачены на это.
RAZOR1703 вне форума Ответить с цитированием
Старый 28.11.2011, 09:35   #4
rpy3uH
добрый няша
Старожил
 
Аватар для rpy3uH
 
Регистрация: 29.10.2006
Сообщений: 4,804
По умолчанию

Цитата:
Сообщение от RAZOR1703 Посмотреть сообщение
Отвечу сразу на первое и на четвертое. Захват hBufSem происходит в нитях в сервере. Этот семафор сигнализирует о том, что якобы буфер пуст и можно писать.
а почему бы не сделать многостроковый буфер, чтобы в него можно было несколько раз записать, не стирая предыдущие записи.
например, в первых 4 байтах общего буфера памяти держать переменную, которая будет сожержать текущий размер данных в буфере, апри записи записывать не в начало, а в конец и после этого обновлять переменную

Код:
PDWORD pDataInbuffSize = (PDWORD)StartMFile;

.....
WaitForSingleObject (hSem, INFINITE);

sprintf (Buf, "Producer №%d produces: %d", num, rand()%2000);
printf ("%s\n", Buf);
int newdatalen = lstrlenA(Buf);//или lstrlenW(Buf)*2;//если юникод
CopyMemory ((PVOID)((DWORD)StartMFile+(*pDataInbuffSize)), Buf, newdatalen);
*pDataInbuffSize += newdatalen;
ReleaseSemaphore (hSem, 1, 0);
при считывании данных с буфера переменная как раз будет содержать размер данных в буфере. После считывания просто обнуляем переменную и вуаля.

Последний раз редактировалось rpy3uH; 28.11.2011 в 19:04.
rpy3uH вне форума Ответить с цитированием
Старый 28.11.2011, 18:29   #5
RAZOR1703
Пользователь
 
Регистрация: 15.12.2010
Сообщений: 74
По умолчанию

Это, конечно здорово, но "по легенде" мне нельзя несколько раз писать. Боюсь, препод мне ататат устроит.
RAZOR1703 вне форума Ответить с цитированием
Старый 28.11.2011, 19:07   #6
rpy3uH
добрый няша
Старожил
 
Аватар для rpy3uH
 
Регистрация: 29.10.2006
Сообщений: 4,804
По умолчанию

ладно, по другому: в клиенте после считывания данных из буфера обнуляй первый байт в нём. в потоках на сервере проверяй, если первый байт не равен нулю (в нём записан какой-то символ) значит писать в него ещё нельзя.
rpy3uH вне форума Ответить с цитированием
Старый 28.11.2011, 20:36   #7
RAZOR1703
Пользователь
 
Регистрация: 15.12.2010
Сообщений: 74
По умолчанию

Сделал. Теперь пишется и читается, все правильно. Только это не решило проблему с тем, что одна нить вообще никогда не пишет. Стабильно пишется и читается, но только с одной нити.
Текст великоват, посему прикрепил.
Вложения
Тип файла: rar Semaph.rar (1.2 Кб, 24 просмотров)
RAZOR1703 вне форума Ответить с цитированием
Старый 28.11.2011, 20:54   #8
rpy3uH
добрый няша
Старожил
 
Аватар для rpy3uH
 
Регистрация: 29.10.2006
Сообщений: 4,804
По умолчанию

Код:
void produser(LPVOID lpParam)
{
	int num = (int) lpParam;
	srand( time(0) );

	do {
		WaitForSingleObject (hSem, INFINITE);
		CopyMemory (Buf, StartMFile, 100);
		if (Buf[0] == 0)
		{
			sprintf (Buf, "Producer #%d produces: %d", num, rand()%2000);
			printf ("%s\n", Buf);
			CopyMemory (StartMFile, Buf, sizeof(Buf) );
			ReleaseSemaphore (hSem, 1, 0);
		}
		else 
		{ 
			ReleaseSemaphore (hSem, 1, 0); 
			break; //<-------
		}

	}
	while (1);
}
один из потоков видит что буфер занят и выходит из цикла, выход из цикла, это выход из процедуры, выход из процедуры это завершение потока.

попробуй вот так
Код:
void produser(LPVOID lpParam)
{
	int num = (int) lpParam;
	srand( time(0) );

	do {
		WaitForSingleObject (hSem, INFINITE);
		CopyMemory (Buf, StartMFile, 100);//копировать не обязательно
		/*можно так
		PCHAR testbuff = (PCHAR)StartMFile;
		if (testbuff[0]==0)
		*/
		if (Buf[0] == 0)
		{
			sprintf (Buf, "Producer #%d produces: %d", num, rand()%2000);
			printf ("%s\n", Buf);
			CopyMemory (StartMFile, Buf, sizeof(Buf) );
			
		}
                       ReleaseSemaphore (hSem, 1, 0);

	}
	while (1);
}
rpy3uH вне форума Ответить с цитированием
Старый 28.11.2011, 21:37   #9
RAZOR1703
Пользователь
 
Регистрация: 15.12.2010
Сообщений: 74
По умолчанию

Да, обидная ошибка. Обидная, как убитая ни на что неделя
Большое спасибо, заработало как надо. Пришлось, правда, рандомный Sleep в нити добавить, чтобы была более наглядная очередность.
RAZOR1703 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
обмен данными между формами zirm Общие вопросы Delphi 4 11.07.2011 22:39
Обмен данными между двумя процессами ReGreed Общие вопросы C/C++ 6 27.05.2011 15:31
Обмен данными между С++ и С# Stellvertreter Общие вопросы C/C++ 0 14.11.2008 14:18
Обмен данными между формами Maks1978 Общие вопросы C/C++ 3 22.07.2008 15:35
Обмен данными между формами Demien Общие вопросы Delphi 14 23.04.2008 22:04