diff --git a/MyList.c b/MyList.c new file mode 100644 index 0000000..1d9daaa --- /dev/null +++ b/MyList.c @@ -0,0 +1,127 @@ +#include +#include +#include + +#include "MyList.h" +#define SIZE 10 // максимальный размер двоичного числа + + +// Функция для инициализации списка +void initList(DoublyLinkedList* list) { + list->head = NULL; + list->tail = NULL; + list->size = 0; +} + + +// Функция выделения памяти под новый узел +Node* createNode(int value) { + Node* newNode = (Node*)malloc(sizeof(Node)); + if (newNode == NULL) { + printf("Memory allocation error!\n"); + exit(1); // аварийное завершение + } + newNode->data = value; + newNode->prev = NULL; + newNode->next = NULL; + return newNode; +} + +// Функция добавления элемента в конец списка +void append(DoublyLinkedList* list, int value) { + Node* newElement = createNode(value); + if (list -> head == NULL){ // если список пустой + newElement -> prev = NULL; + list -> head = newElement; + } + else { // уже не пустой + newElement -> prev = list -> tail; // связь текущего с предыдущим + list -> tail -> next = newElement; // связь предыдущего с текущим + } + list -> tail = newElement; // переопределение конца(указателя) на новый элемент + ++(list -> size); // увеличение списка +} + +// Функция освобождения памяти +void freeList(DoublyLinkedList* list) { + Node* current = list->head; + while (current != NULL) { + Node* temp = current; + current = current->next; // освобождаем память, запоминая указатель на следующий элемент + free(temp); + } + list->head = NULL; // уничтожаем указатели непосредственно списка + list->tail = NULL; + list->size = 0; +} + +// Функция вывода списка от начала к концу +void printForward(const DoublyLinkedList* list){ + Node* current = list -> head; + printf("Dec list: "); + while(current != NULL){ + printf("%d ", current -> data); + current = current -> next; + } + printf("\n"); +} + +// Функция вывода списка от конца к началу +void printBackward(const DoublyLinkedList* list){ + Node* current = list -> tail; + printf("Dec list: "); + while(current != NULL){ + printf("%d ", current -> data); + current = current -> prev; + } + printf("\n"); +} + +void generateRandomList(DoublyLinkedList* list, int count){ + // Инициализация генератора случайных чисел + srand(time(NULL)); + for (int i = 0; i < count; ++i){ + append(list, rand() % 100); + } +} + +// Функция для вывода списка в двоичном виде +void printBinaryValue(const DoublyLinkedList* list) { + printf("Binary list: "); + Node* current = list -> head; + int numberDec; + int count = -1; + while(current != NULL){ + numberDec = current -> data; + int* numberBin = (int*)calloc(SIZE, sizeof(int)); + if (numberBin == NULL){ + printf("Memory allocation error!\n"); + exit(1); + } + //printf("%d ", numberDex); + numberBin[++count] = numberDec % 2; + while (numberDec != 0){ + numberDec /= 2; + numberBin[++count] = numberDec % 2; // добываем 0 и 1 из числа + } + + + // разворачиваем двоичное число + for (int i = 0; i < count / 2; ++i){ + int bubble = numberBin[i]; + numberBin[i] = numberBin[count - 1 - i]; // обмен крайних элементов, двигаемся к середине + numberBin[count - 1 - i] = bubble; + } + // вывод в консоль + for (int i = 0;i < count; ++i){ + printf("%d",numberBin[i]); + } + printf(" "); // пробел между двоичными числами + free(numberBin); // освобождаем память + numberBin = NULL; + numberDec = 0; + count = -1; + current = current -> next; + } + printf("\n"); +} \ No newline at end of file diff --git a/MyList.h b/MyList.h new file mode 100644 index 0000000..713e459 --- /dev/null +++ b/MyList.h @@ -0,0 +1,49 @@ +#ifndef MYLIST_H +#define MYLIST_H + +/* Концепция двухсвязного спиcка + +NULL <- [prev0 data0 next0] <-> [prev1 data1 next1] <-> [prev2 data2 next2] -> NULL + + Node0(head) Node1 Node2(tail) + + +next0 = &Node1 +next1 = &Node2 +next2 = NULL + +prev0 = NULL +prev1 = &Node0 +prev2 = &Node1 + +*/ + +// Cтруктура узла двухсвязного списка (один элемент списка) +typedef struct Node { + int data; + struct Node* prev; // указатель на прерыдущий элемент + struct Node* next; // указатель на следующий элемент +} Node; + +// Структура самого списка +typedef struct { + Node* head; // указатель на первый элемент + Node* tail; // указатель на последний элемент + int size; // счетчик элементов списка +} DoublyLinkedList; + + +void initList(DoublyLinkedList* list); +Node* createNode(int value); +void append(DoublyLinkedList* list, int value); +void freeList(DoublyLinkedList* list); + +void printForward(const DoublyLinkedList* list); +void printBackward(const DoublyLinkedList* list); + +void generateRandomList(DoublyLinkedList* list, int count); + +void printBinaryValue(const DoublyLinkedList* list); + + +#endif // MYLIST_H \ No newline at end of file diff --git a/MyThreads.c b/MyThreads.c new file mode 100644 index 0000000..5d12dff --- /dev/null +++ b/MyThreads.c @@ -0,0 +1,106 @@ +#include +#include +#include "MyThreads.h" + +// Функция для посчета нулевых битов в числе +int countZeroBits(int numberDec){ + int count = 0; + while(numberDec != 0){ + if (numberDec % 2 == 0){ + ++count; + } + numberDec /= 2; + } + return count; +} + +// Функция для подсчета единичных битов в числе +int countOneBits(int numberDec){ + int count = 0; + while(numberDec != 0){ + if (numberDec % 2 == 1){ + ++count; + } + numberDec /= 2; + } + return count; +} +// Функция инициализации и запуска потоков +void threadStart(ThreadArgs* args1, ThreadArgs* args2){ + // Создание потоков + pthread_t thread1; + pthread_t thread2; + + if (pthread_create(&thread1, NULL, threadExecution, args1) != 0) { + printf("Failed to create thread 1"); + exit(1); // аварийное завершение + } + if (pthread_create(&thread2, NULL, threadExecution, args2) != 0) { + printf("Failed to create thread 2"); + exit(1); // аварийное завершение + } + /* разбор функции pthread_create + &thread1 - Указатель на переменную типа pthread_t, в которую будет записан идентификатор созданного потока + NULL - Указатель на атрибуты потока (используем атрибуты по умолчанию) + threadExecution - Функция обработки данных в потоке + &args1 - Указатель на аргументы, которые будут переданы в функцию threadExecution + */ + + // Ожидание завершения потоков + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); +} + +// функция выполнения обработки данных в потоке +void* threadExecution(void* args) { + ThreadArgs* targs = (ThreadArgs*)args; // преобразуем принятые аргументы в структуру + + Node* currentNode = NULL; // объявляем текущий узел + Node* nextNode = NULL; // и следующий + + currentNode = targs -> flag ? targs -> head : targs -> tail; + + while (1) { + + // блокируем список на время чтения узла + pthread_mutex_lock(targs -> listMutex); + + // разблокировка, если конец списка + if (currentNode == NULL) { + pthread_mutex_unlock(targs -> listMutex); + break; + } + + // Сохраняем данные и следующий узел локально + int data = currentNode -> data; + nextNode = targs -> flag ? currentNode -> next : currentNode -> prev; + + // теперь разблокируем список (чтение завершено) + pthread_mutex_unlock(targs -> listMutex); + + // Обработка данных + if (targs -> flag) { + targs -> countZero += countZeroBits(data); + } else { + targs -> countOne += countOneBits(data); + } + ++(targs -> countNodes); + currentNode = nextNode; + } + targs = NULL; + return NULL; +} + +// функция для вывода результата в консоль +void threadPrint(ThreadArgs* args1, ThreadArgs* args2){ + printf("\n"); + printf("Thread 1:\n"); + printf("Zero bits: %d\n", args1 -> countZero); + printf("Count nodes: %d\n", args1 -> countNodes); + printf("\n"); + printf("Thread 2:\n"); + printf("One bits: %d\n", args2 -> countOne); + printf("Count nodes: %d\n", args2 -> countNodes); +} + + diff --git a/MyThreads.h b/MyThreads.h new file mode 100644 index 0000000..d1532e9 --- /dev/null +++ b/MyThreads.h @@ -0,0 +1,22 @@ +#ifndef MYTHREADS_H +#define MYTHREADS_H +#include "MyList.h" + +// Структура аргументов для работы с потоком +typedef struct { + Node* head; // Указатель на голову списка + Node* tail; // Указатель на хвост списка + int countZero; // Счетчик нулевых битов (для потока 1) + int countOne; // Счетчик единичных битов (для потока 2) + int countNodes; // Счетчик обработанных элементов + int flag; // Флаг направления обхода (1 - от головы, 0 - от хвоста) + pthread_mutex_t* listMutex; // Мьютекс для синхронизации доступа к списку +} ThreadArgs; + +int countZeroBits(int numberDec); +int countOneBits(int numberDec); +void threadStart(ThreadArgs* args1, ThreadArgs* args2); +void* threadExecution(void* args); +void threadPrint(ThreadArgs* args1, ThreadArgs* args2); + +#endif //MYTHREADS_H \ No newline at end of file diff --git a/delay.c b/delay.c new file mode 100644 index 0000000..0c5df59 --- /dev/null +++ b/delay.c @@ -0,0 +1,16 @@ +#include "delay.h" + +#ifdef _WIN32 +#include +#else +#include +#endif + +// функция для задержки консоли в Linux или Windows +void delay_ms(int milliseconds) { + #ifdef _WIN32 + Sleep(milliseconds); + #else + usleep(milliseconds * 1000); + #endif +} \ No newline at end of file diff --git a/delay.h b/delay.h new file mode 100644 index 0000000..9e0c050 --- /dev/null +++ b/delay.h @@ -0,0 +1,7 @@ +#ifndef DELAY_H +#define DELAY_H + + +void delay_ms(int milliseconds); + +#endif // DELAY_H \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..4af3a56 --- /dev/null +++ b/main.c @@ -0,0 +1,49 @@ +#include +#include +#include "MyList.h" +#include "delay.h" +#include "MyThreads.h" + + +int main() { + // Подготовка данных + int sizeList = 0; + printf("Enter a number of type int: "); + scanf("%d", &sizeList); + printf("\n"); + DoublyLinkedList MyList; + initList(&MyList); + generateRandomList(&MyList, sizeList); + + // Смотрим на подготовленные данные + printForward(&MyList); // десятичный вид + printBinaryValue(&MyList); // двоичный вид + + // Многопоточность + + // Инициализация мьютекса + pthread_mutex_t listMutex = PTHREAD_MUTEX_INITIALIZER; + /* + Мьютексы нужны для защиты от одновременного + доступа сразу нескольких потоков к одному + обЪекту. Их необходимо использовать, если + предполагается, что список будет не просто + считываться, а изменяться в разных потоках. + + */ + + // Подготовка аргументов для потоков (инициализация структур) + ThreadArgs args1 = {MyList.head, MyList.tail, 0, 0, 0, 1, &listMutex}; // Поток 1 (от головы) + ThreadArgs args2 = {MyList.head, MyList.tail, 0, 0, 0, 0, &listMutex}; // Поток 2 (от хвоста) + + // Запуск многопоточной обработки + threadStart(&args1, &args2); + + // Вывод результатов + threadPrint(&args1, &args2); + + delay_ms(5000); // задержка отображения в консоли (Linux|Windows) + pthread_mutex_destroy(&listMutex); // Уничтожение мьютекса + freeList(&MyList); // освобождение памяти + return 0; +} \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..048f2f1 --- /dev/null +++ b/makefile @@ -0,0 +1,26 @@ +CC = gcc # Используем компилятор gcc +CFLAGS = -Wall -Wextra # Флаги компиляции: включить все предупреждения +LDFLAGS = -lpthread # Флаги линковки: подключить pthread +TARGET = bdz_3 # Имя итогового исполняемого файла + +all: $(TARGET) # Сборка главной цели (TARGET) + +$(TARGET): main.o MyList.o delay.o MyThreads.o # Линковка object-файлов $^ — все зависимости, $@ — цель + $(CC) $^ -o $@ $(LDFLAGS) + +main.o: main.c MyList.h #$< — первая зависимость + $(CC) $(CFLAGS) -c $< -o $@ + +MyList.o: MyList.c MyList.h + $(CC) $(CFLAGS) -c $< -o $@ + +delay.o: delay.c delay.h + $(CC) $(CFLAGS) -c $< -o $@ + +MyThreads.o: MyThreads.c MyThreads.h + $(CC) $(CFLAGS) -c $< -o $@ + +clean: # Удаление всех .o-файлов и бинарника + rm -f *.o $(TARGET) + +.PHONY: all clean \ No newline at end of file