Moi!
Miten toteutetaan intensiivinen prosessi, joka kahmisi itselleen kaiken vapaan suoritinajan? Minulla on nyt ohjelman nopeuttamiseen tälläinen koodi:
For Each arg In Environment.GetCommandLineArgs() If arg = "now" Then 'parametrit Shell("cmd.exe /c ""START /low files.exe""", AppWinStyle.Hide) 'tuon lowin tilalle voi laittaa vaikka realtime tai high, ja silti tulos on aivan sama End 'koeajossa parametrina on "now" End If Next Me.Show() 'nämä Application.DoEvents() 'kolme Me.Activate() 'riviä, koska koodi on form_loadissa ja lopussa on end Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime 'prioriteetiksi 24 System.Threading.Thread.CurrentThread.Priority = Threading.ThreadPriority.Highest 'säikeellekkin kunnollinen prioriteetti Process.GetCurrentProcess().PriorityBoostEnabled = True 'boosti
Mutta silti ohjelma käyttää tehoja vain 25%, joka on sama määrä, jolla jotkut toiset minun ohjelmat pyörivät (niissä ei ole edes noita prioriteettihöskiä)!
F-Securen tarkistusprosessikin (fssm32.exe, F-Secure Scanner Manager 32-bit) käyttää suoritinaikaa parhaimmillaan 99%.
Kokeileppa tehdä useampi säie (thread).
Niistä minulla on erittäin huonoja kokemuksia...
Ohjelmassa on yksi rekursiota hyödyntävä aliohjelma, joka selaa kaikki hakemistot ja tiedostot läpi ja kutsuu erityistä aliohjelmaa joka etsii tiedoston nimestä jotakin merkkijonoja (virusskannerin runko, paitsi että oikeat skannerit kahlaavat tiedostot läpi). Miten tuon toteuttaisin säikeillä? Jokainen hakemistoko eri säikeeseen?
Oletko edes varmistanut, että CPU on rajoittava tekijä? Ellet tee tiedostojen nimille todella ihmeellistä tarkistusta, kovalevyn nopeus on varmasti suurin rajoite.
En myöskään halua, että ilmeisesti taustalla pyörivä ohjelma varaa itselleen kaiken prosessoriajan. Prioriteetin nostamisen sijaan voisit laskea sitä.
Mikäli haluat välttämättä ottaa moniytimisestä prosessorista kaiken irti, joudut käyttämään säikeitä, mutta uskon, että tapauksessasi vaikeudet ovat hyötyjä suuremmat.
-tossu- kirjoitti:
Oletko edes varmistanut, että CPU on rajoittava tekijä? Ellet tee tiedostojen nimille todella ihmeellistä tarkistusta, kovalevyn nopeus on varmasti suurin rajoite.
En oikein älynnyt, mutta ainakaan kiintolevyn nopeus ei tässä mikään rajoite ole. Kun ajan ohjelmaa ensimmäisellä kerralla viimeisimmästä koneen käynnistyksestä, se etsii kiintolevyltä tiedostoja. Mutta sen jälkeen se lukee ainoastaan C:\$Mft -tiedostoa (tilanvaraustaulukko), kunnes käynnistän koneen uudestaan.
-tossu- kirjoitti:
En myöskään halua, että ilmeisesti taustalla pyörivä ohjelma varaa itselleen kaiken prosessoriajan. Prioriteetin nostamisen sijaan voisit laskea sitä.
Ei se toimi taustalla. Kyseessä on siis eräänlainen tiedostojen etsintäohjelma, joka kurkkii kaikkiin paikkoihin, joihin pääsee (erroreita tulee yleensä 216 tai 419).
Ja ne säikeet tosiaan ovat inhottavia.
Yritimpäs vielä niin, että se rekursiolla tomiva aliohjelma tumppaa ne kaikki rekursiot ThreadPooliin. Tulos on tietenkin, että labeli, johon tulokset tulee, päivittyy, mutta ohjelma loppuu ennen aikojaan.
ErroR++ kirjoitti:
Yritimpäs vielä niin, että se rekursiolla tomiva aliohjelma tumppaa ne kaikki rekursiot ThreadPooliin. Tulos on tietenkin, että labeli, johon tulokset tulee, päivittyy, mutta ohjelma loppuu ennen aikojaan.
Pystytkö esittämään sitä formi-loadia ja rekursiota jota koitat ajaa? Vois selventää tilannetta enemmän.
No voinhan nekin tänne laittaa, kunhan pääsisin omalle koneelle.
Ei ole mitään järkeä laittaa jokaista rekursion haaraa omaan säikeeseensä. Ei ole mikään ihme, jos säikeiden maksimimäärä tai muistiraja tulee vastaan.
Sen sijaan voisit laittaa hakemistot pinoon ja käsitellä niitä usealla säikeellä silmukassa. Alussa pitää lisätä pinoon haun ensimmäinen hakemisto. Sitten voit käynnistää haluamasi määrän säikeitä, jotka käyttävät samaa silmukkaa:
Toista: Kesken = kyllä. Ota pinosta seuraava hakemisto. (TryPop) Jos onnistui: Lisää alihakemistot pinoon. (Push) Tee tälle hakemistolle muut temput. Muuten: Kesken = ei. Toista, kun pino on tyhjä: Jos jollain säikeellä on kesken: Odota hetki. Muuten: Lopeta.
Luokka System.Collection.Concurrent.ConcurrentStack toteuttaa monelle säikeelle turvallisen pinon.
Tein koodille pieniä optimointeja ja tässä se koodi:
Public Sub Files(path As String) Try For Each f As FileInfo In New DirectoryInfo(path).GetFiles() lblFiles.Text = f.FullName Application.DoEvents() Next If exitLoop Then fw.Close() : End For Each dir As DirectoryInfo In New DirectoryInfo(path).GetDirectories() lblFiles.Text = dir.FullName Files(dir.FullName) Next Catch ex As Exception totalErrors += 1 End Try End Sub
Koodissa on lisäksi pari muuta osaa joita en nyt tänne viitsi laittaa.
Muuten, tänään tuli uusi kiintolevy WinXP-koneeseen. Ubuntua siihen tällä hetkellä asennan.
Nop hankalapa tuosta on sanoa. Tuo Files ei taida kyllä luoda yhtään uutta threadia.
Jos haluat kokeilla threadpool-käyttöä, niin tuossa olisi sellanen rungon aloitelma, jonka päälle voi loput rakentaa.
(Notepadillä rustattu, joten voi sisältää monennäköistä, mutta korjaileppa siitä)
Nop eipä se ihan notepadillä onnistunut, joten poistin sen. Kokeile tätä... ja muokkaa omaan sopivaksi.
Imports System.Threading Imports System.IO Imports System.Runtime.InteropServices Module Module1 'Sisältää Files taskin parametrit (rakentele setterit ja getterit ja muut puuttuvat) Class TaskParam Public Sub New(ByVal p As String, ByVal astlbl As sendText) path = p st = astlbl End Sub Public path As String 'polku jota tutkitaan Public Delegate Sub sendText(ByVal text As String) 'delegaatti labeli tekstin asettamiseksi dialogiin Public st As sendText End Class Sub Files(ByVal tparam As Object) Dim param As TaskParam = CType(tparam, TaskParam) For Each dir As DirectoryInfo In New DirectoryInfo(param.path).GetDirectories() param.st.Invoke(dir.FullName) ThreadPool.QueueUserWorkItem(AddressOf Files, New TaskParam(dir.FullName, param.st)) Next For Each f As FileInfo In New DirectoryInfo(param.path).GetFiles() param.st.Invoke(f.FullName) Next End Sub 'Tee sub main jotenkin näin 'Pitäs luoda MultiThreadApartment eli monisäikeinen prosessi. (Dialog-projekti ei ole MTA) <MTAThread()> _ Sub Main() Dim dialog As New Form1 'Taskin parametrit Dim tp As New TaskParam("C:\\", AddressOf dialog.asetalabeli) 'asettaa delegaatti-function lebeli-tekstin asettamiseksi Files(tp) ' aloittaa hakemistojen kahlauksen dialog.ShowDialog() ' näyttää dialogin End Sub End Module 'Lisää nämä Formin-koodeihin & tuo TextBox1. Labeliin ei voi tällä kirjoittaa. Public Class Form1 Delegate Sub SetTextCallback(ByVal [text] As String) Public Sub asetalabeli(ByVal tx As String) If Me.TextBox1.InvokeRequired Then Dim d As New SetTextCallback(AddressOf asetalabeli) Me.BeginInvoke(d, New Object() {[tx]}) Else Me.TextBox1.Text = [tx] End If End Sub End Class
Jotenkin näin sen arvelin menevän. Kiitos kaikille!
Lisäys: Paitsi että hidastaa hommaa tuhatkertaisesti ja ei päivitä textilaatikkoa...
Aihe on jo aika vanha, joten et voi enää vastata siihen.