Jak uruchomić wątek w pętli For Each ?

0
 
                   '[chciałbym aby każde i było nowym wątkiem] 
                    For i As Integer = 1 To liczba_watkow
                        uruchom_jeden_watek_dla_pierwszego_maila(i, liczba_watkow)
                    Next

Witam.
Męczę się już cały dzień z aplikacją i na wszelkie sposoby ją zmieniam i nie mam pojęcia jak to zrobić...

Chciałbym aby program działał szybko i jednocześnie uruchamiał kilka wątków naraz....
Dla każdego i z pętli chciałbym zrobić nowy wątek który ma być uruchomiony.. lecz nie mam pojęcia jak to zrobić tak aby razem z argumentami z nawiasów* (i, liczba_watkow)* to uruchomić..
W internecie jedyne co znalazłem to aby robić nowe Threads i później *.Start(), ale to nie jest to , nie da się tam argumentów użyć (bynajmnie ja nie wiem jak)

0

[chciałbym aby każde i było nowym wątkiem]

i nie może być wątkiem bo jest intem.

Nie rozumiem co chcesz zrobić. Czemu do uruchomionego wątku chcesz przekazać liczbę wątków, które mają być uruchomione?
Podaj co ten wątek ma robić/jaką metodę ma wywoływać.

0

@some_ONE

no chcę aby dla każdego i z pętli została wywołana funkcja:

uruchom_jeden_watek_dla_pierwszego_maila(i, liczba_watkow)

gdzie jednym z parametrów w nawiasie jest właśnie i

1

Skoro chodzi o wysłanie maila, a nie o proces, który ma chodzić cały czas w tle, to użyj klasy Task.

1

To zrób tak:

 Task.Run(() => uruchom_jeden_watek_dla_pierwszego_maila(i, liczba_watkow)) 
0

221222222222.png

no i błędy jakieś znowu

próbowałem też:
Tasks.Task.Run(Function() uruchom_jeden_watek_dla_pierwszego_maila(i, liczba_watkow))
i też na nic ;/

0

Bo podałem kod dla C#, nie znam VB.Net. Pewnie za dużo się nie różni, zobacz na MSDN.

No i tam ma być =>, a nie >=.
Dobra, w VB.Net chyba się inaczej robi.
https://msdn.microsoft.com/en-us/library/hh195051(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2

0
    Dim t As Tasks.Task = Tasks.Task.Run(Sub() uruchom_jeden_watek_dla_pierwszego_maila(i, liczba_watkow))

rozumiem że to coś takiego musi być...ale próbuje podstawić moją funkcję tu i nic nie wychodzi ;/

0

W tym zmyślnym języku, który ma chyba więcej słów niż angielski, jak się ma Sub, to trzeba też mieć End Sub, nieprawdaż?

0

w tym przypadku co podałem wyżej, proces niby włącza się ale dalej jakieś komplikacje się pojawiają:

                        Dim T1 As New Tasks.Task((Sub() uruchom_jeden_watek_dla_pierwszego_maila(line, liczba_watkow)))
                        T1.Start()

to niby działa ale pojawiają się kolejne błędy..

nie wiem jak bardzo istotny ten błąd był:

user image

aby wyeliminować błąd to dodałem po prostu linijkę:

 Control.CheckForIllegalCrossThreadCalls = False

i błąd zniknął...

teraz wygląda to tak:

                    For i As Integer = 1 To liczba_watkow
                        Control.CheckForIllegalCrossThreadCalls = False
                        Dim line As String = CStr(i)
                        ' uruchom_jeden_watek_dla_pierwszego_maila(line, liczba_watkow)
                        Dim T1 As New Tasks.Task((Sub() uruchom_jeden_watek_dla_pierwszego_maila(line, liczba_watkow)))
                        T1.Start()
                    Next

no ale program nie działa jak powinien, wręcz chaotycznie pętle działają..

podam też kod który próbuję wywołać tym procesem:

    Private Sub uruchom_jeden_watek_dla_pierwszego_maila(linijkA_login As Integer, plus_ile As Integer)
        linijka = linijkA_login
        plus = plus_ile
        'warunek do spelnienia dla kazdej z linijki RichTextBoxa
        If RichTextBox1.Lines.Count >= linijkA_login Then
            'Przypisanie wartosci email dla Soapu
            email = RichTextBox1.Lines(linijkA_login - 1)
            For Each line In RichTextBox2.Lines
                'Przypisanie 2 wartosci dla wysylania
                source = line.Trim
                ' Wysylanie
                wysylanie(email, source)
            Next

            'Jezeli wszystkie linijki sprawdzone, zmieniamy wartosc A na kolejną


        End If

    End Sub

no i w tej pętli jest wywoływana funkcja:

   Async Sub wysylanie(email, source)
0

Czy to się w ogóle kompiluje?

Dim line As String = CStr(i)
Dim T1 As New Tasks.Task((Sub() uruchom_jeden_watek_dla_pierwszego_maila(line, liczba_watkow)))
Private Sub uruchom_jeden_watek_dla_pierwszego_maila(linijkA_login As Integer, plus_ile As Integer)

Metoda ma przyjmować Integer a przekazujesz String.

0

rzeczywiście małe niedopatrzenie.. ale to jest wynikiem wielu zmian w kodzie, kombinacji itd

Tak naprawdę dla VB.NET chyba nie ma większego znaczenia czy to string czy integer...jeżeli string zawiera liczbę i żadnych innych znaków to może być traktowany jako integer bez wyskoczenia błędu itd

generalnie nie mogę zrozumieć czemu uruchomienie normalne działa dobrze:

  uruchom_jeden_watek_dla_pierwszego_maila(line, liczba_watkow)

natomiast uruchomienie w ten sposób powoduje jakieś dziwaczne działanie

           Dim T1 As New Tasks.Task((Sub() uruchom_jeden_watek_dla_pierwszego_maila(line, liczba_watkow)))
           T1.Start()

nawet jeśli tylko **jeden **wątek uruchamiam (T1.Start) to i tak nie działa to dobrze...

wynikiem końcowym jest wpisanie statusu do TexBoxu np:

"wyslano1, wyslano2, wyslano3" i tak przybywają wpisy z każdą wysłaną wiadomością...
i jeżeli uruchamiam to sposobem z Taskiem to ten TextBox po jakimś czasie sam się czyści....
czyli np dodają się wpisy "wysłano1, wysłano2, wysłano3...." a po jakimś czasie to znika i dodają się nowe "wysłano8, wysłano9...itd"
chociaż żadna funkcja nie ma takiego zadania aby ingerować w TextBox w jaki kolwiek inny sposób niż dodawać status... Nie ma żadnego kasowania zawartości itd...
Po prostu jak dodaje Task powoduje takie niestabilne działania...
jak uruchamiam bez Task to wszystko działa dobrze..

1

jeżeli uruchamiasz taski bazując na jakiejś wspólnej zmiennej w pętli to ważne żebyś pamiętał że zmienna ta będzie współdzielona przez wszystkie wywołania

        static void Main(string[] args)
        {
            for (int i = 0; i < 5; i ++)
            {
                Task.Run(() => Mail(i));
            }
            Console.Read();
        }

        static void Mail(int a)
        {
            Console.WriteLine(a);
        }

w tym przypadku za każdym razem wyświetlone zostanie "5"

żeby skorzystać ze zmiennej musisz przepisać zmienną do lokalnego kontekstu

int j = i;
Task.Run(() => Mail(j)); // działa

tak jest w C#, ale w VB prawdopodobnie tak samo bo ma to sens

0
Czarny Orzeł napisał(a):

"wyslano1, wyslano2, wyslano3" i tak przybywają wpisy z każdą wysłaną wiadomością...
i jeżeli uruchamiam to sposobem z Taskiem to ten TextBox po jakimś czasie sam się czyści....
czyli np dodają się wpisy "wysłano1, wysłano2, wysłano3...." a po jakimś czasie to znika i dodają się nowe "wysłano8, wysłano9...itd"

nie możesz ingerować w interfejs użytkownika z innego wątku - w przeciwnym wypadku dostaniesz wyjątek lub undefined behaviour (jak w Twoim przypadku)
musisz zlecić zmianę interfejsu wątkowi do której przynależy
znam co najmniej kilka sposobów żeby to zrobić w C# ale na VB nie bardzo więc posłuż się google
http://www.codeproject.com/Articles/31985/Updating-the-UI-from-a-thread-The-simplest-way

0
Pijany Terrorysta napisał(a):

nie możesz ingerować w interfejs użytkownika z innego wątku - w przeciwnym wypadku dostaniesz wyjątek lub undefined behaviour (jak w Twoim przypadku)

no ale to nie wątek zmienia bezpośrednio wartości TextBoxa, tylko wywoływana jest funkcja i dopiero ta funkcja zmienia wartości

0
Czarny Orzeł napisał(a):

no ale to nie wątek zmienia bezpośrednio wartości TextBoxa, tylko wywoływana jest funkcja i dopiero ta funkcja zmienia wartości

w jaki sposób wywołujesz tę funkcję? Dopóki jawnie nie przekażesz wywołania tej funkcji do innego wątki (np używając Invoke na dispatcherze / kontekście innego wątku) to funkcja zostaje wywołana w tym samym wątku z którego nastąpiło wywołanie

0

najlepiej do tej pory działała taka kombinacja:

       Dim liczba_watkow = CInt(TextBox1.Text)
        'włączamy jeden wątki
        For i As Integer = 1 To liczba_watkow
            Dim line As String = CStr(i)
            Dim T1 As New Thread(New ThreadStart(Sub() UpdateForm(New Object(1) {"1_watek", line}, Me)))
            T1.Start()
        Next
  Private Delegate Sub UpdateDelegate(ByVal Parameters As Object(), ByVal Thread As Form1)
    Sub UpdateForm(ByVal Parameters As Object(), ByVal Thread As Form1)
        If (InvokeRequired) Then
            Invoke(New UpdateDelegate(AddressOf UpdateForm), Parameters, Thread)
        Else
            Select Case Parameters(0)
                Case "1_watek"
                    wysylanie(Parameters(1), liczba_watkow)
            End Select
        End If
    End Sub

i tutaj niby działa kilka wątków jednoczesnie...
ale...działają one z taką prędkością jak jeden wątek...
po prostu na zmianę działają....

a mi chodzi o to aby równoczesnie pracowały

0

bez sensu - na początku sprawdzasz czy wątek jest uruchomiony z wątku ui i jeśli nie to go uruchamiasz w kontekście wątku ui (czyli stworzony wątek służy tylko do tego żeby zlecić z powrotem pracę wątkowi z którego powstał)
samo wysyłanie maila zostaw w wątku dodatkowym i dopiero na konieć daj Invoke żeby wysłać info o rezultacie na interfejs użytkownika
to co przekazujesz do Invoke musi być szybkie - nie może tam być zawarta cała logika tak jak to robisz teraz

0
T1.Start()

tu włączam wątki...

następnie dla każdego z wątków robię invoke i włączam funkcję wysyłania e-maili
(bez invoke byłyby błędy w funkcji co wysyła te emaile)

Kompletnie nie wiem co tu jest źle

0
Czarny Orzeł napisał(a):

(bez invoke byłyby błędy w funkcji co wysyła te emaile)

Kompletnie nie wiem co tu jest źle

no własnie to
Funkcja wysyłająca maila MUSI być uruchomiona BEZ invoke żeby była wykonana w osobnym wątku
Invoke użyj tylko do wyświetlenia rezultatu (po wysłaniu maila)

Jakie dostajesz błędy przy wysyłaniu maila? Zmień sposób wysyłania maila jeśli ten który masz nie zezwala na uruchamianie z dodatkowych wątków - być może po prostu niektóre z obiektów tworzysz w złym wątku

0

user image

błędy mam takie że bez invoke te funkcje przestają działać, bo pobieram wartości bezpośrednio z RichTextBoxów
(to co na screenie, mam po usunięciu invoke)

0

no tak, wszystko się zgadza - każde odwołanie do richedita musisz zrobić przez Invoke, ale nie całość zadania bo to nie ma sensu.
Odwołań do Invoke może być nawet kilkadziesiąt, ale najlepiej na samym początku do wątku prześlij wszystkie informacje które będą mu potrzebne do pracy (żeby wątki nie musiały niepotrzebnie na siebie czekać), wykonaj pracę, a na końcu prześlij informację o rezultacie do głównego wątku - w tym głównym wątku wyświetl informację w richedicie

Potraktuj wątki jak osoby, jedna zajmuje się pisaniem po tablicy (richedicie), druga wysyłaniem listów
Ta druga może powiedzieć pierwszej (Invoke) żeby w wolnej chwili napisała na tablicy że list został wysłany, ale nie może sama zacząć pisać jednocześnie z pierwszą po tablicy bo będą sobie przeszkadzać

0

zacząłem od nowa pisać aplikację..
i już na dzień dobry mam problem..
W jaki sposób mogę pobrać liczbę linijek z RichTextBox?
Bo zrobiłem coś takiego i niestety nie działa to ;/

    Private Sub uruchom_jeden_watek(zacznij_od_linijki As Integer, plus_ile As Integer)

        MsgBox(pobiez_l_loginow)

    End Sub

       Public Delegate Function pobierz() As String
       Private Function pobiez_l_loginow() As String
        If RichTextBox1.InvokeRequired Then
            Me.Invoke(New pobierz(AddressOf pobiez_l_loginow))
        Else
            pobiez_l_loginow = CStr(RichTextBox1.Lines.Count)

        End If

    End Function
0

Co to znaczy "nie działa"?

0

@ness
funkcja zwraca pusty wynik zamiast liczbę linijek RichTextBoxa
bez invoke działałoby, ale nie wiem jak zastosować invoke aby to zwróciło do Task wartość linijek RichTextBoxa z Form1

0

A to: Me.Invoke(New pobierz(AddressOf pobiez_l_loginow)) nie powinno być przypadkiem poprzedzone returnem? Bo tak to mi wygląda, że wywołujesz funkcję, a wynik zostaje gdzieś w powietrzu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1