[ djura1 @ 03.12.2004. 12:28 ] @
Zdravo svima!
Imam problem sa nitima koji nikako ne mogu da resim. Imam klasu koja nasledjuje Thread i u njoj definisan run() metod:
Code:

 public void run(){
        byte b=0;
        System.out.println("Receiving");
        while(receiving){
            //System.out.println("Run: "+i);
            try{
                b=SerialPort.readByte();
                ReceiveBuffer.write(b);
            }
            catch(IOException ioe){
                System.out.println("I/O error!");
            }
            catch(BufferOverrunException boe){
                System.out.println("Attempt to write outside bufer boundaries!");
            }
        }
    }

Problem je sto, kad pokrenem ovu nit, samo ona se izvrsava, dok je sve ostalo blokirano. Nit pokrecem metodom stratReceiving(), koja izleda ovako:
Code:

public void startReceiving(){
        receiving=true;
        start();
    }

Gde god da sam gledao, u bilo koji primer koda, izgleda mi da sve radim ispravno. Cak i kad pokrenem program u debugeru, on radi, ali kad ga pokrenem normalno, nema sanse.
Ima li neko ideju gde gresim ili neki koristan link u vezi sa nitima?
Hvala unapred.
[ filmil @ 03.12.2004. 12:32 ] @
Citat:
Ima li neko ideju gde gresim ili neki koristan link u vezi sa nitima?
Ne budi ti teško, pošalji ceo program ili najmanji mogući primer koji ima isti problem.

f
[ nsivacki @ 03.12.2004. 16:18 ] @
ako ti program sa nitima radi kako treba u debuggeru, a inace nece da radi, 90% si negde zaboravio neku promenljivu (koju jedna nit menja, a druga je cita) da oznacis sa volatile, i time sprecis kompajler u optimizaciji instrukcija nad tom promenljivom.
Hoces zapravo da forsiras stalno ucitavanje te promenljive, a ne da usled optimizacije ona stalno bude u nekom registru i sadrzi bajatu vrednost.
Debuggeri inace, tu (u ovom slucaju stetnu) optimizaciju ne radi.

[ me-tuzalem @ 05.12.2004. 19:23 ] @
Nije ništa čudno što ti se sve blokira.
Imaš u metodi run beskonačnu petlju, koja čita bajt po bajt sa serijskog porta i sve dok ima bajtova za čitanje teško da će neka druga nit da dobije resurse računara. Zato ti je sve ostalo blokirano.
Kada si u debuggeru, sam debugger kontroliše izvršavanje svih Threadova, pa dok gledaš šta se
izvršava, a šta ne, ostale niti dobiju priliku da odrade svoj deo posla, i ti imaš utisak da program u debuggeru radi, a ovako ne.
Da bi se uverio da li pričam istinu lil lupam, iza
Code:

ReceiveBuffer.write(b); // ubaci
Thread.sleep(10); // samo 1 stotinka !!, a možeš i više da bi bolje proanalizirao stvar


i već će druge niti dobiti priliku da ti pokažu da rade.

Naravno, može problem biti i u tome što serijski port uvek ima šta da pročita. Kada ne i bilo signala na portu, onda ne bi bilo potrebno ubacivati sleep(), već bi JVM stigla da i drugim nitim dodeli malo resursa.
[ djura1 @ 06.12.2004. 07:55 ] @
Izvinjavam se zbog kasnog odgovora, imao sam neku frku ovde.
Uglavnom, izgleda da sam resio sam problem. Stvar je bila u tome sto se druga nit odmah zavrsavala, tako da je ostajala da radi samo ova. Ipak, sad me jedna stvar zbunjuje.

Citat:

Nije ništa čudno što ti se sve blokira.
Imaš u metodi run beskonačnu petlju, koja čita bajt po bajt sa serijskog porta i sve dok ima bajtova za čitanje teško da će neka druga nit da dobije resurse računara. Zato ti je sve ostalo blokirano.


Tacno je da se petlja izvrsava sve dok je receiving==true, ali zar to nije sutina niti? Da se izvrsava nekoliko stvari u isto vreme? Mislim, ova petlja ce stalno da se vrti, ali zar ne bi trebalo da je VM povremeno prekine i pusti neku drugu nit da radi? Ako bih cekao da se petlja zavrsi, onda cela stvar sa nitima nema smisla., mogu isto tako da koristim obicnu funkciju. Ako bi moglo malo pojasnjenje ovoga...

Hvala svima na pomoci.
[ Java Beograd @ 06.12.2004. 08:15 ] @
One se izvrsavaju istovremeno, to omogucava OS, ali je prethodnik hteo da kaze da ce prva nit toliko resursa da zauzme, i toliko procesorskog vremena, da ce druga da se izvrsava samo teoretski istovremeno.
[ djura1 @ 06.12.2004. 08:55 ] @
Citat:

One se izvrsavaju istovremeno, to omogucava OS, ali je prethodnik hteo da kaze da ce prva nit toliko resursa da zauzme, i toliko procesorskog vremena, da ce druga da se izvrsava samo teoretski istovremeno.


OK, ali zar je to samo zbog petlje? U sustini, ona nije beskonacna, vec ce u nekom trenutku promenljiva receiving da se postavi na false, i da prekine petlju. Za to vreme bi obe niti trebalo da se izvrsavaju podjednako cesto (idealno bi bilo posle svakog ciklusa), bar teoretski.
Ili ste hteli da kazete da citanje sa serijskog porta uzima dosta resursa i vremena ili da ima veci prioritet od neke druge operacije?
U svakom slucaju, moj program sad radi relativno zadovoljavajuce, obe niti imaju otprilike isto vreme izvrsavanja, ali bas me zanima da li je to cista sreca ili sam stvarno uradio kako treba.
[ me-tuzalem @ 06.12.2004. 11:47 ] @
Suština niti je da se izvšavaju paralelno( može da liči kao da je istovremeno). To omogućava OS, i kada jedna nit počne da se izvršava, OS joj dodeli resurse. Čim ta nit više nema potrebe za resursima, OS dodeljuje resurse drugoj niti itd. Pri tome nit ne mora da sa završi da bismo rekli da joj više ne trebaju resusi.
Programska petlja(while...) NIJE obavezna u run metodi! Pomoću nje se najlakše ilustruje priča o Threadovima, ali je suština da se u run metodu stavlja nešto što će verovatno da potraje malo duže, a želimo da se neke druge stvari izvršavaju dok se to "duže" izvršava.
A što se tiče problema sa blokiranjem svaki Thread koji ima npr.
Code:

public void run(){
     int i = 0;
    while(true){
         i++;
    }
}

će da blokira ostale niti, jer kada taj Thread "mazne" resurse (najčešće CPU) ostali mogu samo da se slikaju.
Znači, kod u Threadu treba da se osloni na neki asinhroni događaj koji će da ga malo uspori, i samim time oslobodi resurse za druge niti, ili programski da sa yield() metodom pruži šansu drugim nitima.
Samo čitanje serijskog porta nije kritično, ali su tebi verovatno neprestano stizali bajtovi, i zato se resursi nisu oslobađali. Ako želiš to da proveriš, pokreni program, pa izvadi kabl iz serijskog porta i videćeš da se druga nit pokrenula.
[ filmil @ 06.12.2004. 12:49 ] @
Nešto mi u ovim objašnjenjima nije jasno: naime, koliko ja znam, Javin scheduler za niti je best effort, što znači da se svakoj od niti koja je spremna da radi dodeljuje fiksni vremenski „odsečak“ u kome se izvrši deo metoda run(). Niti koje čekaju na neku I/O operaciju nisu u listi spremnih pa ih scheduler ne raspoređuje za izvršenje sve dok se ne desi događaj na koji čekaju.

Ako međutim imamo više niti koje su spremne, one efektivno dele resurse (recimo jedinog) procesora.

Ako postoji jedna nit koja stalno radi, ukupan efekat ne bi trebao da bude da su sve ostale niti zaustavljene, već da računar prividno radi sporije. (npr ako ima 3 niti koje rade na računaru od 100MHz, efektivni takt svake od njih je oko 33MHz, verovatno nešto manje usled režijskog vremena). Dakle nikako ne bi trebalo da se desi da jedna nit uzme sve procesorsko vreme, možda samo da uspori odziv sistema.

Drugo, iz perspektive računara serijski port je jaaako spor, pa sve i da odonud konstantno pristižu znaci, opet nekako ne deluje logično da se ostalo zamrzne dok radi čitanje. Kao što znaju svi koji surfuju internetom preko modema, računaru nije nikakav problem da predaje i prima znake sa modema i još da ima vremena da vozi program kojim korisnik gleda web prezentacije.  Zato mi je čudan zaključak da se nit zaključa jer serijska veza stalno radi.

Pre će biti da negde postoji nekakav semafor koji je (slučajno ili namerno) zaključan. Zato čovek treba da pošalje ceo kod, jer je to mnogo bolje za pomoć od metode gledanja u ništa.

f
[ djura1 @ 06.12.2004. 13:22 ] @
@me-tuzalem:

Ne slazem se sa ovim. Na primer:
Code:

public class ThreadTest{
    public static void main(String[] args){
        Th1 t1=new Th1();
        Th2 t2=new Th2();
        
        t1.start();
        t2.start();
    }
}

class Th1 extends Thread{
    public void run(){
        while(true){
            System.out.println("Th1");
        }
    }
}

class Th2 extends Thread{
    public void run(){
        while(true){
            System.out.println("Th2");
        }
    }
}

Kada pokrenem ovaj program, na izlazu dobijem nekoliko puta "Th1", zatim nekoliko puta "Th2", pa opet "Th1"....Znaci da nijedna nit nije blokirala drugu.
[ djura1 @ 06.12.2004. 13:29 ] @
OK, evo i kompletnog koda:

kalsa MainTest
Code:

public static void main(String[] args) throws Exception{
        
        
        try{
            SerialPort port=new SerialPort("COM1",SerialPortConstants.BR_57600,SerialPortConstants.DB_8B,
                SerialPortConstants.SB_1B,SerialPortConstants.PY_NONE);
            final ReceiveBuffer buffer=new ReceiveBuffer(100000);
            Receiver rcv=new Receiver();
            
            class TimerHandler implements ActionListener{
                public void actionPerformed(ActionEvent e){
                    try{
                    buffer.writeFlag();
                    }
                    catch(BufferOverrunException boe){
                        boe.printStackTrace();
                    }
                }
            }
            Timer timer=new Timer(5000,new TimerHandler());
            timer.setRepeats(true);
            buffer.reset();
            rcv.startReceiving();
            timer.start();
            
            while(buffer.available()==0){
                System.out.println("No data");
                continue;
            }
            while(buffer.available()>0){
                System.out.println("data"+ReceiveBuffer.read());
            }
            rcv.stopReceiving();
        }
        catch(Exception ex){
            ex.printStackTrace();
        }

klasa Receiver
Code:

public class Receiver extends Thread{
    
    private boolean receiving;
    
    public Receiver() {
        super();
        receiving=false;
    }
    
    
    public void run(){
        short b=0;
        System.out.println("Receiving");
        while(receiving){
            System.out.println("Run: ");
            try{
                b=SerialPort.readByte();
                ReceiveBuffer.write(b);
            }
            catch(IOException ioe){
                System.out.println("I/O error!");
            }
            catch(BufferOverrunException boe){
                System.out.println("Attempt to write outside bufer boundaries!");
            }
        }
    }
    
   
    public void startReceiving(){
        receiving=true;
        start();
    }
    
    
    public void stopReceiving(){
        receiving=false;
    }  
}

klasa ReceievBuffer
Code:

public class ReceiveBuffer {
    
    private static short[] buffer;
    private static int readMarker,writeMarker;
    private boolean eventFlag;
    
    
    public ReceiveBuffer() {
        buffer=new short[1000];
        readMarker=0;
        writeMarker=0;
        eventFlag=false;
    }
    
   
    public ReceiveBuffer(int size){
        buffer=new short[size];
        readMarker=0;
        writeMarker=0;
        eventFlag=false;
    }
    
   
    public static synchronized void write(short data) throws BufferOverrunException{
        if(writeMarker>buffer.length-1){
            throw new BufferOverrunException("write marker out of bounds");
        }
        //if data=-1, don't write anything
        if(data==-1){
            System.out.println("No data");
            return;
        }
        //write data to marker position
        buffer[writeMarker]=data;
        //move marker for one place
        writeMarker++;
    }
    
   
    public static synchronized short read() throws BufferOverrunException,MarkerOverrunException{
        if(readMarker>buffer.length-1)
            throw new BufferOverrunException("Read marker out of bounds");
        if(readMarker>writeMarker)
            throw new MarkerOverrunException("Read marker exceeded write marker");
        short data=buffer[readMarker];
        buffer[readMarker]=0;
        readMarker++;
        return data;
    }
    
    
    public int available(){
        return (writeMarker-readMarker);
    }
    
   
    public void reset(){
        readMarker=0;
        writeMarker=0;
    }
    
    
    public synchronized void writeFlag() throws BufferOverrunException{
        if(writeMarker>buffer.length-1)
            throw new BufferOverrunException("Write marker outside buffer range!");
        buffer[writeMarker]=300;
        writeMarker++;
    }
    
}


Napominjem jos jednom d program RADI, tj. izvrsavaju se obe niti, ali zbog cele ove price, vise ne znam sta da mislim. U svakom slucaju, ako neko primeti neku gresku, bio bih zahvalan na savetu.