morgot
Интересующийся
Offline
|
|
« : 09-01-2013 22:39 » |
|
Приветствую всех участников форума. Возникла проблема с потоками, собственно вопрос больше в тему WinApi, нежели Асм, но т.к. пишу на Асме, то решил запостить сюда. Есть код (тестовый), который получает на вход путь к папке, ищет рекурсивно в ней другие папки, и выводит на консоль. Если находит новую папку, создает для нее поток, и так ищет. Есть лимит на к-тво одновременно работающих потоков. Проблема в том, что почему-то семафоры не освобождаются - т.е. запускаются, скажем, 3 потока и висят. В чем может быть дело, подскажите пожалуйста: .686 .model flat,stdcall option casemap:none
find_files proto :dword Crlf PROTO
include \masm32\include\windows.inc include \masm32\macros\macros.asm uselib kernel32,user32,masm32,msvcrt includelib \masm32\lib\irvine32.lib
.data treadN dd 0 ;глобальный счетчик mask0 db "*",0
.data? hSem dd ?
.code main proc local fh,fs1ze,heapH,pbuf,bwr:dword
invoke CreateSemaphore,0,3,3,0 mov hSem,eax
invoke find_files,chr$("F:\Demod\txtdata") exit main endp
find_files proc path:dword local wfd:WIN32_FIND_DATA ;структура для поиска local hFindFile,localN:dword local buf[MAX_PATH]:byte; //рабочий буфер local directory[MAX_PATH]:byte; //текущая директория local directoryRes[MAX_PATH]:byte; //резервный буфер
invoke InterlockedIncrement,offset treadN mov localN,eax; //дублируем значение
;invoke WaitForSingleObject,hSem,INFINITE invoke crt_printf,chr$("thread num started - %d, path - "),treadN ;invoke Crlf invoke StdOut,path invoke Crlf
invoke lstrcpy,addr buf,path invoke lstrcpy,addr directory,path ;addr не нужен? invoke lstrcpy,addr directoryRes,path
invoke lstrcat,addr buf,chr$("\") invoke lstrcat,addr buf,offset mask0 invoke FindFirstFile,addr buf,addr wfd mov hFindFile,eax inc eax jz @err ;invoke lstrcmp,addr wfd.cFileName,
.if wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ;если это папка invoke lstrcmp,addr wfd.cFileName,chr$(".") ;это "."? идем дальше test eax,eax jz @next invoke lstrcmp,addr wfd.cFileName,chr$("..") ;это ".."? также пропускаем test eax,eax jz @next invoke lstrcat,addr directory,chr$("\") invoke lstrcat,addr directory,addr wfd.cFileName ;добавляем к текущей папке имя найденной invoke find_files,addr directory invoke CreateThread,0,0,offset find_files,addr directory, 0, 0 invoke CloseHandle,eax invoke lstrcpy,addr directory,addr directoryRes jmp @next .endif
@next: invoke FindNextFile,hFindFile,addr wfd test eax,eax jz @ex .if wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ;если это папка invoke lstrcmp,addr wfd.cFileName,chr$(".") ;это "."? идем дальше test eax,eax jz @next invoke lstrcmp,addr wfd.cFileName,chr$("..") ;это ".."? также пропускаем test eax,eax jz @next invoke lstrcat,addr directory,chr$("\") invoke lstrcat,addr directory,addr wfd.cFileName ;добавляем к текущей папке имя найденной invoke find_files,addr directory invoke lstrcpy,addr directory,addr directoryRes jmp @next .endif jmp @next
invoke ReleaseSemaphore,hSem,1,0 invoke crt_printf,chr$("thread num closed - %d"),localN ;тред завершен invoke Crlf @err: fn MessageBox,0,LastError$(),"Last Error Text",MB_OK invoke StdOut,addr directory invoke Crlf @ex: ;invoke ExitThread,0 ret find_files endp end main А если раскоментировать ;invoke ExitThread,0, то сканирует всего две папки и завершается.
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #1 : 09-01-2013 23:41 » |
|
Offtopic: Всегда было интересно, какая религия предписывает программировать под Windows на ассемблере. Поставлю в угол.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
zubr
Гость
|
|
« Ответ #2 : 10-01-2013 03:39 » |
|
А потому что ты рекурсивно зацикливаешь объекты синхронизации. При вызове вложенного потока больше 3 он стоит, на этом рекурсивный цикл останавливается и не вызывает ReleaseSemaphore.
|
|
|
Записан
|
|
|
|
morgot
Интересующийся
Offline
|
|
« Ответ #3 : 10-01-2013 09:40 » |
|
При вызове вложенного потока больше 3 он стоит, на этом рекурсивный цикл останавливается и не вызывает ReleaseSemaphore. Но почему так? Я думал, что механизм такой - поток вызывает вложенный поток, а сам (родительский поток), в это время, дальше продолжает сканировать файлы, доходит до ReleaseSemaphore и освобождает его, в итоге дочерный поток может работать. Или где я ошибся? И как исправить? Dimkaоффтоп с макросами, имхо, не сложнее Си, а кое где даже проще.
|
|
|
Записан
|
|
|
|
zubr
Гость
|
|
« Ответ #4 : 10-01-2013 11:32 » |
|
Посмотрел внимательнее: 1. .if wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ;если это папка invoke lstrcmp,addr wfd.cFileName,chr$(".") ;это "."? идем дальше test eax,eax jz @next invoke lstrcmp,addr wfd.cFileName,chr$("..") ;это ".."? также пропускаем test eax,eax jz @next invoke lstrcat,addr directory,chr$("\") invoke lstrcat,addr directory,addr wfd.cFileName ;добавляем к текущей папке имя найденной invoke find_files,addr directory invoke CreateThread,0,0,offset find_files,addr directory, 0, 0 invoke CloseHandle,eax invoke lstrcpy,addr directory,addr directoryRes jmp @next .endif
Зачем ты здесь вызываешь invoke find_files,addr directory а затем invoke CreateThread,0,0,offset find_files,addr directory, 0, 0 ? 2. Не вижу вызова FindClose по завершению поиска. 3. По идее у тебя сам рекурсивный процесс не останавливается, а потокам приходится долго ждать пока до них дойдет очередь, смотри п. 1.
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #5 : 10-01-2013 11:44 » |
|
Dimka оффтоп Цитата с макросами, имхо, не сложнее Си, а кое где даже проще. Вот ты сам в своём коде запутался, хотя задача элементарная, а я даже вчитываться не стал в эти переходы по меткам вместо нормально структурированного кода. Это при том, что современный компилятор C сгенерирует машинный код оптимальнее вручную написанного. Ты б алгоритм написал на человеческом языке, прежде чем на ассемблере код писать. И ответил на два вопроса: 1) Как синхронизировать вывод на экран разных потоков? 2) Как синхронизировать завершение работы всех потоков дерева обхода?
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
morgot
Интересующийся
Offline
|
|
« Ответ #6 : 10-01-2013 12:36 » |
|
zubr, спасибо, буду исправлять - все невнимательность, кодил ночью. Dimka, я пробовал накодить это на Си, и там запутался еще больше. Там функция требует volatile, приведение типов и так далее. В Си тоже есть свои минусы, одна типизация чего стоит. 1) Как синхронизировать вывод на экран разных потоков? 2) Как синхронизировать завершение работы всех потоков дерева обхода? 1. вход в крит. секцию - вывод - выход с крит. секции. 2. пока не знаю.
|
|
|
Записан
|
|
|
|
morgot
Интересующийся
Offline
|
|
« Ответ #7 : 10-01-2013 13:07 » |
|
Вроде получилось, если вдруг кому надо - вот код. .686 .model flat,stdcall option casemap:none
find_files proto :dword Crlf PROTO DumpRegs PROTO
include \masm32\include\windows.inc include \masm32\macros\macros.asm uselib kernel32,user32,masm32,msvcrt includelib \masm32\lib\irvine32.lib
.data treadN dd 0 ;глобальный счетчик mask0 db "*",0 crit_sec CRITICAL_SECTION <>
.data? hSem dd ?
.code main proc local fh,fs1ze,heapH,pbuf,bwr:dword
invoke InitializeCriticalSection,offset crit_sec
invoke CreateSemaphore,0,3,3,0 mov hSem,eax
invoke CreateThread,0,0,offset find_files,chr$("F:\Demod\txtdata"), 0, 0 invoke CloseHandle,eax invoke Sleep,20000 invoke ExitProcess,0 main endp
find_files proc path:dword local wfd:WIN32_FIND_DATA ;структура для поиска local hFindFile,localN:dword local buf[MAX_PATH]:byte; //рабочий буфер local directory[MAX_PATH]:byte; //текущая директория local directoryRes[MAX_PATH]:byte; //резервный буфер
invoke InterlockedIncrement,offset treadN mov localN,eax; //дублируем значение @@: invoke TryEnterCriticalSection,offset crit_sec .if eax invoke lstrcpy,addr buf,path invoke LeaveCriticalSection,offset crit_sec .else invoke Sleep,0 jmp @b .endif invoke lstrcpy,addr directory,addr buf invoke lstrcpy,addr directoryRes,addr buf invoke lstrcat,addr buf,chr$("\") invoke lstrcat,addr buf,offset mask0 invoke StdOut,chr$("init dir - ") invoke StdOut,addr buf invoke Crlf
invoke WaitForSingleObject,hSem,INFINITE
invoke FindFirstFile,addr buf,addr wfd mov hFindFile,eax inc eax jz @err
@find: .if wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ;если это папка invoke lstrcmp,addr wfd.cFileName,chr$(".") ;это "."? идем дальше test eax,eax jz @next invoke lstrcmp,addr wfd.cFileName,chr$("..") ;это ".."? также пропускаем test eax,eax jz @next invoke lstrcat,addr directory,chr$("\") invoke lstrcat,addr directory,addr wfd.cFileName ;добавляем к текущей папке имя найденной ;invoke find_files,addr directory invoke CreateThread,0,0,offset find_files,addr directory,0,0 invoke CloseHandle,eax invoke Sleep,1000 @@: invoke TryEnterCriticalSection,offset crit_sec .if eax invoke lstrcpy,addr directory,addr directoryRes invoke LeaveCriticalSection,offset crit_sec .else invoke StdOut,chr$("cannot enter crit sect") invoke Sleep,1000 jmp @b .endif .endif @next: invoke FindNextFile,hFindFile,addr wfd test eax,eax jz @ex jmp @find
@err: fn MessageBox,0,LastError$(),"Last Error Text",MB_OK invoke StdOut,chr$("fatal error") @ex: invoke crt_printf,chr$("thread num closed - %d"),localN ;тред завершен invoke StdOut,addr directory invoke Crlf
invoke FindClose,hFindFile invoke ReleaseSemaphore,hSem,1,0 invoke crt_printf,chr$("Semaphore value - %d",0Dh,0Ah),eax ;тред завершен invoke ExitThread,0
find_files endp end main
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #8 : 10-01-2013 14:03 » |
|
Dimka, я пробовал накодить это на Си, и там запутался еще больше. Там функция требует volatile, приведение типов и так далее. В Си тоже есть свои минусы, одна типизация чего стоит. Хм... Интересный подход - чтоб проще было и без запар. Отчего б тогда не писать, скажем, на Ruby? require 'thread'
################################################################################
@out_mutex = Mutex.new
def out(string) @out_mutex.lock puts string @out_mutex.unlock end
################################################################################ def proc_dir(path) out path threads = [] begin dir = Dir.open path for name in dir if name != '.' and name != '..' then path = dir.path + (dir.path.slice(-1..-1) != '/' ? '/' : '') + name if File.directory?(path) then threads.push(Thread.new { proc_dir path }) else out path end end end dir.close rescue end threads.each { |thread| thread.join } end
################################################################################
proc_dir "c:/" Кода в разы меньше, а структурное программирование сохранено. И никакой типизации и всего такого.
|
|
« Последнее редактирование: 10-01-2013 14:07 от Dimka »
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
morgot
Интересующийся
Offline
|
|
« Ответ #9 : 10-01-2013 15:25 » |
|
Можно и на Руби (хотя я люблю Перл ), но в данном случае мне нужен именно ехе, причем не зависимый от всяческих библиотек типа NET фреймворк.
|
|
|
Записан
|
|
|
|
Dimka
Деятель
Команда клуба
Offline
Пол:
|
|
« Ответ #10 : 10-01-2013 15:32 » |
|
morgot, дык наверно надо было на Perl написать - отладить алгоритм, а потом уже переносить на ассемблер.
|
|
|
Записан
|
Программировать - значит понимать (К. Нюгард) Невывернутое лучше, чем вправленное (М. Аврелий) Многие готовы скорее умереть, чем подумать (Б. Рассел)
|
|
|
|