Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Проблема с семафором (потоки)  (Прочитано 16861 раз)
0 Пользователей и 6 Гостей смотрят эту тему.
morgot
Интересующийся

ch
Offline Offline

« : 09-01-2013 22:39 » 

Приветствую всех участников форума.
Возникла проблема с потоками, собственно вопрос больше в тему WinApi, нежели Асм, но т.к. пишу на Асме, то решил запостить сюда. Есть код (тестовый), который получает на вход путь к папке, ищет рекурсивно в ней другие папки, и выводит на консоль. Если находит новую папку, создает для нее поток, и так ищет. Есть лимит на к-тво одновременно работающих потоков. Проблема в том, что почему-то семафоры не освобождаются - т.е. запускаются, скажем, 3 потока и висят. В чем может быть дело, подскажите пожалуйста:

Код: (ASM)
.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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 09-01-2013 23:41 » 

Offtopic:
Всегда было интересно, какая религия предписывает программировать под Windows на ассемблере.
Поставлю в угол.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
zubr
Гость
« Ответ #2 : 10-01-2013 03:39 » 

А потому что ты рекурсивно зацикливаешь объекты синхронизации. При вызове вложенного потока больше 3 он стоит, на этом рекурсивный цикл останавливается и не вызывает ReleaseSemaphore.
Записан
morgot
Интересующийся

ch
Offline Offline

« Ответ #3 : 10-01-2013 09:40 » 

При вызове вложенного потока больше 3 он стоит, на этом рекурсивный цикл останавливается и не вызывает ReleaseSemaphore.
Но почему так? Я думал, что механизм такой - поток вызывает вложенный поток, а сам (родительский поток), в это время, дальше продолжает сканировать файлы, доходит до ReleaseSemaphore и освобождает его, в итоге дочерный поток может работать. Или где я ошибся? И как исправить?

Dimka
оффтоп
Цитата
с макросами, имхо, не сложнее Си, а кое где даже проще.
Записан
zubr
Гость
« Ответ #4 : 10-01-2013 11:32 » new

Посмотрел внимательнее:
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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 10-01-2013 11:44 » 

Цитата: morgot
Dimka
оффтоп
Цитата
с макросами, имхо, не сложнее Си, а кое где даже проще.
Вот ты сам в своём коде запутался, хотя задача элементарная, а я даже вчитываться не стал в эти переходы по меткам вместо нормально структурированного кода. Это при том, что современный компилятор C сгенерирует машинный код оптимальнее вручную написанного.

Ты б алгоритм написал на человеческом языке, прежде чем на ассемблере код писать. И ответил на два вопроса:
1) Как синхронизировать вывод на экран разных потоков?
2) Как синхронизировать завершение работы всех потоков дерева обхода?

Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
morgot
Интересующийся

ch
Offline Offline

« Ответ #6 : 10-01-2013 12:36 » 

zubr, спасибо, буду исправлять - все невнимательность, кодил ночью.

Dimka, я пробовал накодить это на Си, и там запутался еще больше. Там функция требует volatile, приведение типов и так далее. В Си тоже есть свои минусы, одна типизация чего стоит.
Цитата
1) Как синхронизировать вывод на экран разных потоков?
2) Как синхронизировать завершение работы всех потоков дерева обхода?
1. вход в крит. секцию - вывод - выход с крит. секции.
2. пока не знаю.
Записан
morgot
Интересующийся

ch
Offline Offline

« Ответ #7 : 10-01-2013 13:07 » 

Вроде получилось, если вдруг кому надо - вот код.

Код: (ASM)
.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
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 10-01-2013 14:03 » 

Цитата: morgot
Dimka, я пробовал накодить это на Си, и там запутался еще больше. Там функция требует volatile, приведение типов и так далее. В Си тоже есть свои минусы, одна типизация чего стоит.
Хм... Интересный подход - чтоб проще было и без запар. Отчего б тогда не писать, скажем, на Ruby?

Код: (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
Интересующийся

ch
Offline Offline

« Ответ #9 : 10-01-2013 15:25 » 

Можно и на Руби (хотя я люблю Перл Улыбаюсь ), но в данном случае мне нужен именно ехе, причем не зависимый от всяческих библиотек типа NET фреймворк.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 10-01-2013 15:32 » 

morgot, дык наверно надо было на Perl написать - отладить алгоритм, а потом уже переносить на ассемблер.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines