Консультация № 182146
09.02.2011, 09:12
53.55 руб.
0 5 1
Здравствуйте, уважаемые эксперты! Прошу вас ответить на следующий вопрос:Помогите написать программу на языке С++. Распишите какие объекты используются, подробно про комментируйте основные части алгоритма и при возможности приложите файл с готовой программой.
Условие задачи:
4 объекта хаотично движутся в одном окне.
- Форма объекта значение не имеет. Квадраты, треугольники, кружочки, на Ваше усмотрение.
- Направление и скорость значение не имеют.
- Главное, что каждый объект должен отрисовываться отдельным потоком (thread).
- Используя объекты синхронизации, добиться того, чтобы объекты не наезжали друг на друга

Заранее Большое Спасибо!!!!!!!!!!!!

Обсуждение

давно
Старший Модератор
17042
808
09.02.2011, 13:07
общий
Адресаты:
И как всегда... Компилятор / среда разработки? Целевая операционная система? Не в первый раз ведь вопросы задаёте, читайте памятку!
Об авторе:
We have but faith: we cannot know;
For knowledge is of things we see;
And yet we trust it comes from thee,
A beam in darkness: let it grow.
-----
https://www.linkedin.com/in/andreynkuznetsov
https://www.researchgate.net/profile/Andrey_Kuznetsov11
http://www.researcherid.com/rid/K-8824-2014
давно
Посетитель
276566
297
09.02.2011, 15:01
общий
Адресаты:
OC Windows, Borland Builder C++
давно
Профессор
230118
3054
10.02.2011, 12:57
общий
Адресаты:
Эта задача все-таки достаточно сложная.
Неизвестный
15.02.2011, 16:55
общий
это ответ
Здравствуйте, Magma!
Вот пример под Builder 6.0. Я так понимаю, задание на использование api-функций синхронизации. В качестве объекта синхронизации используем событие (event). Сначала надо его создать функцией CreateEvent.
Эта функция создаёт или открывает существующее событие, возвращаемым значением является его описатель.
Код:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, //Аттрибуты защиты
BOOL bManualReset, //Ручной сброс
BOOL bInitialState, //Состояние при создании
LPCTSTR lpName //Имя (если необходимо)
);

Параметры функции:
lpEventAttributes - в качестве этого параметра передаётся адрес структуры вида:
Код:
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength; //Размер структуры (как правило, берётся sizeof)
LPVOID lpSecurityDescriptor; //Указатель на дескриптор, контролирующий совместное //использование объекта
BOOL bInheritHandle; //Флаг, разрешающий наследование объекта дочерним процессом
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;

bManualReset - если этот параметр true, то помещение объекта в несигнальное состояние нужно производить вручную (путём вызова специальной функции). Если же передать сюда false, то событие будет обладать автосбросом, т.е. автоматически возвращаться в несигнальное состояние.
bInitialState - если этот параметр равен true, начальное состояние события - сигнальное, в противном случае - несигнальное.
Более подробную информацию можно получить в MSDN.
В данном случае вызов такой:
[b]hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);[/b]
т.е. автосброс и исходное состояние - сигнальное.
Чтобы делать это событие сигнальным (что мы будем делать, как только надо будет освободить ресурс, в данном случае - область рисования), будем вызывать функцию [b]SetEvent (hEvent)[/b].
Также необходимо, собственно, дождаться, когда ресурс будет свободен, т.е. событие станет сигнальным. Для этого предусмотрен ряд wait-функций, нас в данном случае интересует WaitForSingleObject. Эта функция приостанавливает выполнение текущего потока до тех пор, пока указанный в её параметрах объект ядра не придёт в сигнальное состояние (также возможен выход по таймауту). Применяется для ограниничения доступа к разделяемым ресурсам. Параметром чаще всего служит событие, семафор или мьютекс, но также в качестве параметра может выступать уведомление (notification), процесс, поток и некоторые другие объекты. Возвращаемое значение показывает, что послужило выходом из функции. Это может быть WAIT_ABANDONED (специфическое значение, возвращаемое в случае завершения потока, владеющего мьютексом), WAIT_OBJECT_0 (объект получил сигнальное состояние - в этом случае функция сама переводит его в несигнальное, а для семафора наращивает счётчик числа занятых ресурсов), WAIT_TIMEOUT (функция вышла по таймауту).
Код:
DWORD WaitForSingleObject(
HANDLE hHandle, //Описатель объекта ядра, сигнальное состояние которого нас интересует
DWORD dwMilliseconds //Таймаут, по истечению которого прервать ожидание
);

Параметры функции:
dwMilliseconds - здесь можно указать любой необходимый таймаут, кроме того, если указать 0, функция вернёт потоку управление сразу (таким образом можно проверить состояние объекта), а если указать константу INFINITE, функция не будет учитывать время и будет ждать только сигнального состояния объекта ядра.
Более подробную информацию можно получить в MSDN.
Нам нужно вызывать её внутри потока при каждой итерации вот так:
[b]WaitForSingleObject (hEvent, INFINITE)[/b]
И, наконец, нам необходимо созадавать потоки, которые будут рисовать. Для этого используем CreateThread.
Эта функция создаёт поток внутри адресного пространства текущего процесса. Возвращает описатель созданного потока. Функция полезна, когда, например, нужно выполнять какие-то действия в оконном приложении, но не отключать при этом его интерфейс. В общем случае поток начинает работу сразу после вызова этой функции, и все действия, которые он выполняет, не мешают работе программы.
Код:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //Аттрибуты защиты
SIZE_T dwStackSize, //Размер стека потока (если указать 0, то берётся по умолчанию)
LPTHREAD_START_ROUTINE lpStartAddress, //Адрес стартовой функции
LPVOID lpParameter, //Параметр для передачи потоку
DWORD dwCreationFlags, //Служебные флаги
LPDWORD lpThreadId //ID потока
);

Параметры функции:
lpStartAddress - указатель на стартовую функцию потока (т.е. её название), которую надо объявить внутри приложения. Она должна иметь определённый вид:
Код:
DWORD WINAPI ThreadProc( //Имя функции может быть произвольным 
LPVOID lpParameter
);

lpParameter - параметр, передаваемый в стартовую функцию потока. Если потоку необходимо передать несколько параметров, следует либо использовать глобальные переменные, либо объявлять структуру со всеми необходимыми данными и передавать её.
dwCreationFlags - в основном этот параметр используется для того, чтобы обозначить, следует ли запустить поток сразу после создания или по вызову функции ResumeThread (в первом случае в параметре можно передать 0, во втором - флаг CREATE_SUSPENDED).
lpThreadId - возвращаемый параметр - id потока, который иногда может быть полезен (например, если потоку будет посылаться сообщение). Если id не нужен, можно передать NULL.
Более подробную информацию можно получить в MSDN.
Мы будем создавать потоки в цикле так:
[b]CloseHandle(CreateThread(NULL, 0, WorkThread, (void*)obj[i], 0, &dwID));[/b]
CloseHandle пишем, чтобы описатель сразу закрылся, потому что мы не будем им пользоваться (сам поток при это не будет завершён, закрывается только handle).

Рисовать будем разноцветные квадраты на Image. Всё остальное должно быть ясно из комментариев.
Удачи!

Приложение:
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

struct Data { //данные для потока
int id; //id объекта
TColor fon; //цвет фона
int w; //ширина области рисования
int h; //высота области рисования
};

const obj_count = 4; //число объектов
HANDLE hEvent; //событие для синхронизации
int coords[obj_count][4]; //координаты углов 4-х объектов
TColor curCol[obj_count] = {clGreen, clRed, clBlack, clBlue}; //цвета объектов

void sPaint (bool erase, int id = -1, TColor col = clWhite) //отрисовка объектов и стирание
{
if (!erase || id<0) { //если не задано стирание или не передан id объекта - рисуем
for (int i=0; i<obj_count; i++) {
Form1->Image1->Canvas->Pen->Color = curCol[i];
Form1->Image1->Canvas->Brush->Color = curCol[i];
Form1->Image1->Canvas->Rectangle(coords[i][0], coords[i][1], coords[i][2], coords[i][3]);
}
} else { //иначе - затираем заданный объект заданным цветом
Form1->Image1->Canvas->Pen->Color = col;
Form1->Image1->Canvas->Brush->Color = col;
Form1->Image1->Canvas->Rectangle(coords[id][0], coords[id][1], coords[id][2], coords[id][3]);
}
}

DWORD WINAPI WorkThread (void *pParam) //Рабочий поток
{
TCanvas* cnvs = Form1->Image1->Canvas; //сохраняем ссылку для краткости
Data* dt = (Data*)pParam; //приводим к исходному типу данные
bool ok = false; //флаг
int dim[4]; //координаты углов
srand (time(NULL)); //инициализируем генератор случайных чисел
while (true) {
WaitForSingleObject (hEvent, INFINITE); //ждём освобождения ресурса
ok = false; //сбрасываем флаг для цикла
while (!ok) { //повторяем, пока не найдём свободное место
ok = true; //принимаем координаты за верные
dim[0] = rand()%(dt->w-70); //выбираем случайные координаты
dim[1] = rand()%(dt->h-70); //в пределах области рисования
dim[2] = dim[0]+50; //рисуем квадраты со стороной 50
dim[3] = dim[1]+50;
for (int i=dim[0]; i<=dim[2]; i+=50) //проверяем, чтобы место было свободно
for (int j=dim[1]; j<=dim[3]; j+=50) //достаточно проверить углы
if (cnvs->Pixels[i][j]!=curCol[dt->id] && cnvs->Pixels[i][j]!=dt->fon) ok = false;
//если пиксель не текущего цвета и не цвета фона, значит место занято
}
sPaint (true, dt->id, dt->fon); //стираем объект
for (int i=0; i<4; i++) //сохраняем новые координаты
coords[dt->id][i] = dim[i];
sPaint (false); //рисуем новоерасположение объекта
SetEvent (hEvent); //освобождаем ресурс
Sleep (500+rand()%(2000-500)); //засыпаем на случайное время от 500 до 2000 мс
//можно сделать в зависимости от номера объекта (500*(dt->id+1));
//или вообще одинаковое число, но тогда объекты будут перемещаться вместе
}
return 0;
}


//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------


void __fastcall TForm1::FormCreate(TObject *Sender)
{
hEvent = CreateEvent(NULL, FALSE, TRUE, NULL); //Создаём событие с неручным сбросом и сигнальным состоянием
coords[0][0] = 0; coords[0][1] = 0; coords[0][2] = 50; coords[0][3] = 50;
coords[1][0] = 50; coords[1][1] = 50; coords[1][2] = 100; coords[1][3] = 100;
coords[2][0] = 100; coords[2][1] = 100; coords[2][2] = 150; coords[2][3] = 150;
coords[3][0] = 150; coords[3][1] = 150; coords[3][2] = 200; coords[3][3] = 200;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender) //по нажатию кнопки
{
Button1->Visible = false; //убираем её
DWORD dwID;
Data** obj = new Data* [obj_count]; //создаём данные для потоков
for (int i=0; i<obj_count; i++) {
obj[i] = new Data; //выделяем память под экземпляр
obj[i]->w = this->Image1->ClientWidth; //сохраняем размерности image
obj[i]->h = this->Image1->ClientHeight;
obj[i]->fon = this->Image1->Canvas->Pixels[0][0]; //сохраняем цвет фона
obj[i]->id = i; //заполняем идентификатор
//создаём потоки со стартовой функцией WorkThread и параметром pParam=obj[i]
CloseHandle(CreateThread(NULL, 0, WorkThread, (void*)obj[i], 0, &dwID));
}
}
//---------------------------------------------------------------------------

Неизвестный
15.02.2011, 16:58
общий
Адресаты:
Вот ещё сам проект
Прикрепленные файлы:
0e10aec21656172b12469010901d841e.zip
Форма ответа