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 'boostiMutta 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 SubKoodissa 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 ClassJotenkin 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.