hilpers


  hilpers > comp.lang.* > comp.lang.java

 #1  
11.03.2010, 22:53
Jochen Theodorou
Hi alle,

also das Thema ist ein relativ schweres und ich wollte hier mal nach ein
paar praktischen Erfahrungen fragen. Zum Beispiel wie ihr zu
"piggybacking with synchronization" steht. Aber erst mal was anderes
vielleicht...

Für eine Datenstruktur hier habe mir folgendes Muster ausgedacht:

[..]
> private DataStructure init() {
> //....
> return new DataStructure();
> }
>
> public void clearDataStructure() {
> unsynchronizedPointer = null;
> synchronized(this) { synchronizedPointer=null; }
> }
> }




Die Anwendung wird nun in vielen Threads gleichzeitig getDataStructure()
aufrufen und hin und wieder wird die DatenStruktur gelöscht. Da
getDataStructure() extrem oft aufgerufen wird würde ein synchonisieren
hier dafür sorgen eine Menge Rechenleistung zu verlieren, und das auch
schon bei nur einem einzigen Thread.

Mein nächster Schritt wäre das explizite clearDataStructure() in ein
implizites um zu wandeln.. Dann wäre das vielleicht eher sowas:

[..]
> synchronizedPointer = new WeakReference<DataStructure>(sp);
> unsynchronizedPointer = synchronizedPointer;
> return sp;
> }
> }
>
> private DataStructure init() {
> //....
> }
> }


Nun meine ich dass es theoretisch möglich ist, dass
unsynchronizedPointer ein nicht vollständig initialisiertes Objekt
bekommt, oder nicht?

Meine erste Idee war dies zu tun:

[..]
> synchronizedPointer = new WeakReference<DataStructure>(sp);
> }
> unsynchronizedPointer = synchronizedPointer;
> return sp;
> }
>
> private DataStructure init() {
> //....
> }
> }


da die Zuweisung nach dem synchronized block erfolgt und dieser selbst
sicherstellt, dass synchronizedPointer ein vollständiges Objekt bekommt,
welches nach Ende des Blocks auch Verfügbar ist... solltes dies doch
eigentlich sicher sein... oder?

Gruss theo
 #2  
12.03.2010, 00:35
Patrick Roemer
Responding to Jochen Theodorou:

>> DataStructure sp = synchronizedPointer.get();
>> synchronized(this) {
>> if (sp==null) sp = init();
>> synchronizedPointer = new WeakReference<DataStructure>(sp);
>> }
>> unsynchronizedPointer = synchronizedPointer;
>> return sp;


So kannst Du doch wiederum keine Garantie dafuer ableiten, dass der
Zugriff auf synchronizedPointer in der ersten Zeile ein vollstaendig
initialisiertes Objekt sieht, oder?

Viele Gruesse,
Patrick
 #3  
12.03.2010, 02:37
Wanja Gayk
Am 12.03.2010, 00:53 Uhr, schrieb Jochen Theodorou <blackdrag>:

>
> Die Anwendung wird nun in vielen Threads gleichzeitig getDataStructure()
> aufrufen und hin und wieder wird die DatenStruktur gelöscht. Da
> getDataStructure() extrem oft aufgerufen wird würde ein synchonisieren
> hier dafür sorgen eine Menge Rechenleistung zu verlieren, und das auch
> schon bei nur einem einzigen Thread.


Ist das eine Vermutung oder hast du das gemessen?


>> public DataStructure getDataStructure() {
>> DataStructure usp = unsynchronizedPointer.get();
>> if (usp!=null) return usp;
>> synchronized(this) {
>> DataStructure sp = synchronizedPointer.get();
>> if (sp==null) sp = init();
>> synchronizedPointer = new WeakReference<DataStructure>(sp);
>> unsynchronizedPointer = synchronizedPointer;
>> return sp;
>> }
>> }


Erinnert mich irgendwie an double checked locking und ich bin mir fast
sicher, dass es sogar das gleiche Problem hat, solange
"unsynchronizedPointer" nicht volatile ist.

> Nun meine ich dass es theoretisch möglich ist, dass
> unsynchronizedPointer ein nicht vollständig initialisiertes Objekt
> bekommt, oder nicht?


Das meine ich auch.


>
> da die Zuweisung nach dem synchronized block erfolgt und dieser selbst
> sicherstellt, dass synchronizedPointer ein vollständiges Objekt bekommt,
> welches nach Ende des Blocks auch Verfügbar ist... solltes dies doch
> eigentlich sicher sein... oder?


Das ist eine Fehlannahme, siehe:
http://www.cs.umd.edu/~pugh/java/mem...edLocking.html
unter "A fix that doesn't work".

"Unfortunately, that intuition is absolutely wrong. The rules for
synchronization don't work that way. The rule for a monitorexit (i.e.,
releasing synchronization) is that actions before the monitorexit must be
performed before the monitor is released. However, there is no rule which
says that actions after the monitorexit may not be done before the monitor
is released. It is perfectly reasonable and legal for the compiler to move
the assignment helper = h; inside the synchronized block, in which case we
are back where we were previously. Many processors offer instructions that
perform this kind of one-way memory barrier. Changing the semantics to
require releasing a lock to be a full memory barrier would have
performance penalties."

In deinem Fall ist es also legal, dass der Compiler "unsynchronizedPointer
= synchronizedPointer;" in den synchronized-Block zieht.

Ich frage mich beim Blick auf den synchonized Block auch, warum du nicht
auf ein Lock-Objekt für dieses Feld synchronisierst. Gesetzt der Fall du
hast mehrere Felder auf die synchroniert zugegriffen werdeb muss, würdest
du mit synchronized(this) auf das ganze objekt locken, nicht nur auf das
Feld.

Also eher so:

private final syncPointerLock = new Object();
private DataStructure synchronizedPointer = ...

DataStructure getDataStructure(){
synchronized(syncPointerLock){
DataStructure sp = synchronizedPointer.get();
if(sp == null) sp = init();
synchronizedPointer = new WeakReference<DataStructure>(sp);
return sp;
}
}

Wenn du auf die WeakReference verzichtest und das Löschen wieder explizit
machst, kannst du dir vielleicht auch was mit einer
AtomicMarkableReference bauen, statt mit synchronization, aber ich würde
es mich an Josh Bloch halten:

"
If you need high-performance lazy initializing of an instance field, use
the double-check idiom with a volatile field. This idiom wasn't guaranteed
to work until release 5.0, when the platform got a new memory model. The
idiom is very fast but also complicated and delicate, so don't be tempted
to modify it in any way. Just copy and paste -- normally not a good idea,
but appropriate here:

// Double-check idiom for lazy initialization of instance fields.
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized(this) {
result = field;
if (result == null) // Second check (with locking)
field = result = computeFieldValue();
}
}
return result;
}
"
http://java.sun.com/developer/techni...ive_08_qa.html

Was das synchronized in dem Snippet angeht: ich würde, wie gesagt, nicht
auf "this" locken, sondern auf ein Lock für genau dieses Feld.

Gruß,
-Wanja-

(schlaflos)
 #4  
12.03.2010, 07:48
Thorsten Nitz
Am 12.03.2010 00:53, schrieb Jochen Theodorou:

> Für eine Datenstruktur hier habe mir folgendes Muster ausgedacht:
>


Zuweisung an Referenzen sind AFAIR atomar, daher kannst Du die
Synchronisation weglassen, wenn Du die Referenz volatile machst. Das ist
notwendig, weil es nicht reicht, der Variablen etwas zuzuweisen, der
neue Inhalt muss auch an die anderen Threads übertragen werden. Diese
dürfen nämlich Kopien von Objekten cachen.

Etwa so:

class MultithreadDataStrcutureHandler {
private volatile DataStructure pointer;

public getDataStructure()
{
return pointer;
}

public void init()
{
DataStructure newData = DataStructure();
// Weitere Initialisierung

// Hier ist newData vollständig erzeugt, es muss nur noch
// zugänglich gemacht werden
pointer = newData; // atomar
}

// Konstruktor ruft init()
}

Du kannst dann gelegentlich init() aufrufen, wenn Änderungen anstehen.
Das funktioniert, solange init() nicht konkurrierend aufgerufen wird.
Wenn das der Fall ist, müsstest Du entweder die Erzeugung von newData in
init() synchronisieren oder den Compare-and-Swap-Mechanismus aus der
concurrent-Bibliothek verwenden.

Das andere grundsäzliche Problem mit diesem Design ist, dass Benutzer
von MultithreadDataStrcutureHandler nicht notwendig die neuesten Daten
verarbeiten:

MultithreadDataStrcutureHandler myHandler = ...;
DataStructure ds = myHandler.getDataStructure();
// Verbringe viel Zeit mit was anderem

// Hier ist ds möglicherweise längst veraltet:
System.out.println( "Der Wert ist "
+ ds.x
+ " - ob das noch stimmt?" );

> Da
> getDataStructure() extrem oft aufgerufen wird würde ein synchonisieren
> hier dafür sorgen eine Menge Rechenleistung zu verlieren, und das auch
> schon bei nur einem einzigen Thread.


Generell gibt es keinen Ersatz für Synchronisation, der das gleiche
leistet. Die Alternativen volatile und concurrent-Package grefen nur da,
wo man am entscheidenden Punkt eine Variable von einfachem, kurzem
Datentyp umsetzen kann.

Unter http://www.angelikalanger.com/Articles/Topics.html findest Du ein
paar Artikel von Frau Langer zum Thema Concurrent Programming,
insbesondere die aus dem JavaMagazig von Juli - Dezember 2008. Ich
erinnere mich, im letzten Jahr in irgendwelchen Print-Ausgaben auch was
über das java.util.concurrent-Package gelesen zu haben, weiß aber die
Ausgaben nicht mehr. Wenn Du das rausfindest und nachliest, erfährst Du
auch, an welchen Stellen ich mich geirrt habe ;-)

Tschö, wa!
Thorsten
 #5  
12.03.2010, 08:05
Bernd Hohmann
Jochen Theodorou wrote:

> also das Thema ist ein relativ schweres und ich wollte hier mal nach ein
> paar praktischen Erfahrungen fragen. [...]
>
> Die Anwendung wird nun in vielen Threads gleichzeitig getDataStructure()
> aufrufen und hin und wieder wird die DatenStruktur gelöscht. Da
> getDataStructure() extrem oft aufgerufen wird würde ein synchonisieren
> hier dafür sorgen eine Menge Rechenleistung zu verlieren, und das auch
> schon bei nur einem einzigen Thread.


So aus der Praxis:

- synchronized() verbrät nicht viel Rechenleistung im Sinne von
CPU-Zyklen sondern Du verbrätst höchstens Zeit verbraten wenn andere
warten müssen. Und das ist nicht viel in Deiner Konstruktion - ausser
neue Daten werden bereitgestellt.

- Wenn das Singleton .getDataStructure() so oft aufgerufen wird, dass
dir schon die synchronisation Bauchschmerzen bereitet, dann ist die
Konstruktion wohl nicht die Richtige. In diesem Fall würde ich die Daten
ganz weit vorne hochziehen ehe die Threads loslaufen und die Daten im
Thread selber halten statt jedesmal vom Singelton neu zu beziehen.

- Für den Austausch der Daten (der ja wohl eher seltener Auftritt als
der Aufruf von .getDataStructure()) würde ich mir ein Flag in
MultithreadDataStrcutureHandler anlegen was Du in den Threads genau dann
abfragen kannst, wenn Du auch bereit bist mit den neuen Daten zu arbeiten.

Also:

MultithreadDataStrcutureHandler.init();
for (...) new MeinOllerThread()

MeinOllerThread
run() {
MultithreadDataStrcutureHandler
local=MultithreadDataStrcutureHandler.getData()
while(true) {
.wait()
if (shutdown) break;
if (MultithreadDataStrcutureHandler.hasNew())
local=MultithreadDataStrcutureHandler.getData()
... machwas ...
}
}

Wenn Du noch etwas Unübersichtilichkeit (aka Eleganz) reinbringen
willst: Registriere "MeinOllerThread" in MultithreadDataStrcutureHandler
und setze dort ein Notify ab wenn sich die Daten geändert haben.

Bernd
 #6  
12.03.2010, 10:43
Jochen Theodorou
Patrick Roemer wrote:
> Responding to Jochen Theodorou:
>
>>> DataStructure sp = synchronizedPointer.get();
>>> synchronized(this) {
>>> if (sp==null) sp = init();
>>> synchronizedPointer = new WeakReference<DataStructure>(sp);
>>> }
>>> unsynchronizedPointer = synchronizedPointer;
>>> return sp;

>
> So kannst Du doch wiederum keine Garantie dafuer ableiten, dass der
> Zugriff auf synchronizedPointer in der ersten Zeile ein vollstaendig
> initialisiertes Objekt sieht, oder?


hmm.. stimmt... die Zuweisung muss nach dem Block erfolgen... hmm...
eventuell brauche ich synchronizedPointer dann gar nicht:

>>> DataStructure sp = synchronizedPointer.get();
>>> WeakReference<DataStructure> ref = null
>>> synchronized(this) {
>>> if (sp==null) sp = init();
>>> ref = new WeakReference<DataStructure>(sp);
>>> }
>>> synchronizedPointer = ref
>>> unsynchronizedPointer = synchronizedPointer;
>>> return sp;


wäre es dann...

Gruss theo
 #7  
12.03.2010, 10:59
Jochen Theodorou
Wanja Gayk wrote:
> Am 12.03.2010, 00:53 Uhr, schrieb Jochen Theodorou <blackdrag>:
>
>> Die Anwendung wird nun in vielen Threads gleichzeitig
>> getDataStructure() aufrufen und hin und wieder wird die DatenStruktur
>> gelöscht. Da getDataStructure() extrem oft aufgerufen wird würde ein
>> synchonisieren hier dafür sorgen eine Menge Rechenleistung zu
>> verlieren, und das auch schon bei nur einem einzigen Thread.

>
> Ist das eine Vermutung oder hast du das gemessen?


gemessen... ein Vorschlag... mach mal ein fibonacci, welches auf einem
volatile-Feld rechnet. Es ist noch nicht einmal, dass volatile oder
synchronized alleine. Es ist das Problem, dass dann Hotspot hier jede
Menge Optimierungen einfach nicht mehr durchführen kann.

[...]
[..]
> monitor is released. It is perfectly reasonable and legal for the
> compiler to move the assignment helper = h; inside the synchronized
> block, in which case we are back where we were previously. Many
> processors offer instructions that perform this kind of one-way memory
> barrier. Changing the semantics to require releasing a lock to be a full
> memory barrier would have performance penalties."
>
> In deinem Fall ist es also legal, dass der Compiler
> "unsynchronizedPointer = synchronizedPointer;" in den synchronized-Block
> zieht.


ich dachte mir doch da stimmt etwas noch nicht... so ein mist.

> Ich frage mich beim Blick auf den synchonized Block auch, warum du nicht
> auf ein Lock-Objekt für dieses Feld synchronisierst. Gesetzt der Fall du
> hast mehrere Felder auf die synchroniert zugegriffen werdeb muss,
> würdest du mit synchronized(this) auf das ganze objekt locken, nicht nur
> auf das Feld.


Das habe ich gemacht um das Beispiel kleiner zu halten.

> Also eher so:
>
> private final syncPointerLock = new Object();
> private DataStructure synchronizedPointer = ...
>
> DataStructure getDataStructure(){
> synchronized(syncPointerLock){
> DataStructure sp = synchronizedPointer.get();
> if(sp == null) sp = init();
> synchronizedPointer = new WeakReference<DataStructure>(sp);
> return sp;
> }
> }
>
> Wenn du auf die WeakReference verzichtest und das Löschen wieder
> explizit machst, kannst du dir vielleicht auch was mit einer
> AtomicMarkableReference bauen, statt mit synchronization, aber ich würde
> es mich an Josh Bloch halten:[...]


Was ich haben will ist allerdings keine traditionelle Faktory, auch
keine die das Lazy macht. Es ist für absolut in Ordnung wenn zwei
Threads die Initialisierung parallel ausführen... sofern mir das am Ende
mehr Performance bringt. Normalerweise würde ich das komplett ohne
synchronization machen und dafür nur Objekte verwenden, die
ausschließlich final Felder haben. Allerdings muss ich sher viele
interne Datenstrukturen ändern und auf einige von denen habe ich
keinerlei Einfluss. Das heisst ich brauche unbedingt eine Lösung mit
Synchronisierung, nur soll die mir nicht ständig in die Quere kommen.

Gruss theo
 #8  
12.03.2010, 11:06
Jochen Theodorou
Thorsten Nitz wrote:
> Am 12.03.2010 00:53, schrieb Jochen Theodorou:

[...]
>
> Zuweisung an Referenzen sind AFAIR atomar, daher kannst Du die
> Synchronisation weglassen, wenn Du die Referenz volatile machst. Das ist
> notwendig, weil es nicht reicht, der Variablen etwas zuzuweisen, der
> neue Inhalt muss auch an die anderen Threads übertragen werden. Diese
> dürfen nämlich Kopien von Objekten cachen.


stimmt, für ein =null ist das nicht notwendig. Gut das Beispiel ist
nicht so ganz ideal, denn die Löschung muss nicht, oder überhaupt in
anderen Threads sichtbar werden. Die Idee dahinter ist, dass man
Huckepack mit einer anderen Synchronisaton die Änderung zu einem
passenden Zeitpunkt propagieren wird. Daher ist es absolut ok, wenn die
Referenz nicht volatile ist... selbst wenn das später dazu führen wird,
dass mehrere initialisierungen durchgeführt wernden werden.

[...]
> Das andere grundsäzliche Problem mit diesem Design ist, dass Benutzer
> von MultithreadDataStrcutureHandler nicht notwendig die neuesten Daten
> verarbeiten:


Wie gesagt... das ist in meinem Fall ok. Oder besser gesagt, das ist ein
Nachteil, den ich bereit bin einzugehenund das Design der Applikation
entsprechend anzupassen, dass dies erlaubt ist. Dass es dann irgendwo
dann doch einmal Synchronisierungspunkte geben ist klar. Allerdings will
ich so zu sagen "externe Synchronisierung" machen.

Gruss theo
 #9  
12.03.2010, 11:13
Jochen Theodorou
Bernd Hohmann wrote:
> Jochen Theodorou wrote:

[...]
> - synchronized() verbrät nicht viel Rechenleistung im Sinne von
> CPU-Zyklen sondern Du verbrätst höchstens Zeit verbraten wenn andere
> warten müssen. Und das ist nicht viel in Deiner Konstruktion - ausser
> neue Daten werden bereitgestellt.


Ok, ich schätze ich muss das einmal anders erklären. Es geht hier
eigentlich um die Groovy-Runtime. Für jeden Methodenaufruf aus Groovy
heraus werden in etwa 7 Stellen durchlaufen, an denen Synchronisierung
statt findet. Auf einem Mehrprozessorsystem bedeutet das 7
Speichersynchronisierungen pro Methodenaufruf. Und das ist einfach nicht
akzeptabel. Aber auch schon im Falle eines einzigen Threads verhindert
jedes volatile und jedes synchronized das Hotspot den Code optimiert.
Und auch das möchte ich so nicht akzeptieren.

> - Wenn das Singleton .getDataStructure() so oft aufgerufen wird, dass
> dir schon die synchronisation Bauchschmerzen bereitet, dann ist die
> Konstruktion wohl nicht die Richtige. In diesem Fall würde ich die Daten
> ganz weit vorne hochziehen ehe die Threads loslaufen und die Daten im
> Thread selber halten statt jedesmal vom Singelton neu zu beziehen.


Eigentlich ist das mehr eine Art Cache, denn ein Singleton. Es dürfen
also durchaus mehrere Instanzen existieren. Die Daten alle am Anfang zu
bestimmen geht dann natürlich nicht.

Gruss theo
 #10  
13.03.2010, 19:31
Wanja Gayk
Jochen Theodorou said...

> Was ich haben will ist allerdings keine traditionelle Faktory, auch
> keine die das Lazy macht. Es ist für absolut in Ordnung wenn zwei
> Threads die Initialisierung parallel ausführen... sofern mir das am Ende
> mehr Performance bringt.


Hmmm.. da die meisten Zugriffe wohl lesend sein werden, hast du es mal
mit einem ReentrantReadWriteLock versucht?

T value;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

T get(){
try{
rwl.readLock().lock();
if(value == null){
try{
//lock-upgrade
rwl.readLock().unlock(); //readlock aufgeben
rwl.writeLock().lock(); //writelock holen
if(value == null){ //nochmal checken, diesmal unter writelock
value = init();
}
}finally{
// lock von write auf readlock downgraden
rwl.readLock().lock();
rwl.writeLock().unlock();
}
}
return value;
}finally{ //readLock aufgeben
rwl.readLock().unlock();
}
}

T müsste man jetzt durch ne WeakReference ersetzen.
Ich befürchte aber fast, dass du auch dort nicht um eine volatile
Referenz herum kommst.

Gruß,
-Wanja-
 #11  
13.03.2010, 19:36
Wanja Gayk
Jochen Theodorou said...

> Eigentlich ist das mehr eine Art Cache, denn ein Singleton. Es dürfen
> also durchaus mehrere Instanzen existieren. Die Daten alle am Anfang zu
> bestimmen geht dann natürlich nicht.


Irgendwie geistert die ganze Zeit "ThreadLocal" in meinem Kopf herum..

Gruß,
-Wanja-
 #12  
18.03.2010, 09:47
Heiner Kücker
Jochen Theodorou wrote:
> Ok, ich schätze ich muss das einmal anders erklären. Es geht hier
> eigentlich um die Groovy-Runtime. Für jeden Methodenaufruf aus Groovy
> heraus werden in etwa 7 Stellen durchlaufen, an denen Synchronisierung
> statt findet. Auf einem Mehrprozessorsystem bedeutet das 7
> Speichersynchronisierungen pro Methodenaufruf. Und das ist einfach nicht
> akzeptabel. Aber auch schon im Falle eines einzigen Threads verhindert
> jedes volatile und jedes synchronized das Hotspot den Code optimiert.
> Und auch das möchte ich so nicht akzeptieren.
>
> Eigentlich ist das mehr eine Art Cache, denn ein Singleton. Es dürfen
> also durchaus mehrere Instanzen existieren. Die Daten alle am Anfang zu
> bestimmen geht dann natürlich nicht.


Ich würde bei allen Referenzen, bei denen es möglich ist,
den Modifier

final

benutzen.

Entsprechend dem Java Memory Model wird eine final-Referenz
nur einmal in den lokalen Memory eines Threads gelesen.
Der lokale Memory eines Threads wird technisch wahrscheinlich
auf den Cache eines CPU-Kerns abgebildet.

Dagegen müssen volatile-Referenzen bei jedem Zugriff zwischen
Heap und Thread Memory abgeglichen werden, was weitgehend
einem synchronisierten Zugriff entspricht.

Natürlich können nicht alle Referenzen final sein, dann wäre
Deine Datenstruktur unveränderlich.

Also als Regel entweder final oder volatile benutzen.

Aber wenn nur eine (oder wenige) Stelle(n) in Deinem
Objekt-Graphen volatile sind, dann hast Du einen guten
Kompromiss zwischen Performance und Sicherheit.

Dazu sollte aber Dein Code eine Art Executions-Klammer
einhalten (das Wort Transaktions-Klammer passt hier nicht,
es geht nicht um Transaktionen).

Am Beginn dieser Klammer wird die Wurzel des
Objekt-Graphen besorgt (volatile) und innerhalb der
Executions-Klammer mit den Kind-Elementen des
Objekt-Graphen gearbeitet (final).

Damit realisiert die Datenstruktur so etwas ähnliches
wie die CopyOnWriteArrayList aus dem JDK-API.

Innerhalb der Executions-Klammer wird immer
nur ein einziges Exemplar des Objekt-Graphen
verwendet.

Eine neue Executions-Klammer kann einen
neuen Objekt-Graphen bekommen.

Wichtig ist dabei, dass keine Unter-Struktur des
Objekt-Graphen ausserhalb der Executions-Klammer
dauerhaft referenziert wird.

Diese Idee ist übrigens nicht neu.
Ich habe vor etlichen Jahren mal mit einem Kollegen
über CMS diskutiert und er hatte vorgeschlagen,
die zu statifizierenden Änderungen in eine(mehrere)
temporäre Datei(en) zu schreiben und den ganzen
Kram durch Umbenennen mit einem Schlag
wirksam zu machen.

> Gruss theo


Gruss
Heiner
www.heinerkuecker.de
Ähnliche Themen
XML mit Datenstrukturen in VBS einlesen.

Ich habe folgendes XML file, das Datenstrukturen beschreibt: <IFD> <CID> <Block Name="Block"> <Group Name="Group"> <Variable BlockNumber="1" Name="A" Description="Text"...

rechteckige Datenstrukturen

hi, kennt jemand von euch eine moeglichkeit eine Tabelle zu speichern in der ich dann wahlweise auf ganze Spalten oder Reihen zugreifen kann. Etwas in der art: class table<I,...

Speichern großer Datenmengen / Großer Dateien

Hallo Gemeinde, ich habe eine warscheinlich einfache Frage: Wenn ich eine große Datenmenge (z.B. MP3 Sammlung oder ISO-Images Sammlung) in WSS 2.0 in eine Dokumenten...

Rückgabe von Datenstrukturen

Moin, sagen wir mal, ich habe eine Datenstruktur, zum Beispiel einen Baum und möchte daraus nach bestimmten Kriterien Elemente raussuchen und als Liste zurückliefern. Meine...


Alle Zeitangaben in WEZ. Es ist jetzt 23:32 Uhr. | Privacy Policy