Laden...

Forenbeiträge von Dunedain Ingesamt 41 Beiträge

21.03.2016 - 16:35 Uhr

Sooo...

Hab nun wieder etwas Zeit gehabt. Leider waren meine Taten nicht vom gewünschten Erfolg gekrönt. Da Kerberos vom verstehen und von der Nutzung der WinAPI nicht wirklich einfach ist, habe ich zum testen das "klist" Tool aus dem Windows 2003 Server Resource Kit verwendet.

Dann habe ich eine Verbindung zur NAS hergestellt (kein Mapping, direkter Zugriff via \blubb)*net use zeigt die Verbindung an *klist dagegen nicht (Cached tickets: 0)

Daher habe ich mich etwas mehr mit net use befasst. Die zuvor genannte Verbindung wird bei net use aufgelistet und lässt sich mit net use \blubb /delete auch löschen. Leider ändert das nichts daran, dass ich mit dem Windows Explorer noch immer Zugriff habe, OHNE mich erneut anmelden zu müssen.
Das net use ... /delete Äquivalent WNetCancelConnection2(name, flags, force) mit/ohne CONNECT_UPDATE_PROFILE bzw. Force-Parameter haben daran leider nichts geändert...

14.03.2016 - 16:42 Uhr

Erstmal danke für die schnelle Antwort 😃 - hatte bei dem (wie ich finde) ungewöhnlichen Thema nicht so schnell mit einer Antwort gerechnet.

Habe deinen Rat beherzigt und mich über das verlinkte Struct schlau gemacht - die ersten Treffer sehen dem überfliegen nach sehr vielversprechend aus! Ich hoffe, dass ich mich zeitnah wieder damit beschäftigen kann...
...was bei Freizeit-Projekten so eine Sache ist...

13.03.2016 - 16:39 Uhr

...tja, ich hoffe, ich habe mich im Titel verständlich ausgedrückt 😃

Das Szenario ist wie folgt:1.Man meldet sich im Windows Explorer an einer Netzwerk-Resource an (z.B.: \MeinServer) 1.Das Häkchen bei "Anmeldedaten speichern" ist NICHT gesetzt 1.Dadurch das die Anmeldedaten nicht gespeichert wurden, gibt es keinen Eintrag im Windows Tresor 1.net use meldet "Es sind keine Einträge in der Liste" 1.Die gecachten Anmeldedaten bleiben erhalten, bis der aktuelle Nutzer sich abmeldet (denke ich zumindest, hab auch gelesen dass es helfen kann, alle Explorer Fenster zu schließen - das ist aber keine Option)

Ich möchte jetzt gezielt für eine Netzwerk-Resource die gecachten Anmeldedaten verwerfen, so dass diese - wenn das nächste mal auf sie zugegriffen wird, wieder eine Anmeldung erfordert. Idealer Weise sollte auch ein laufender Zugriff (Kopier- bzw. Schreibvorgang) unterbrochen werden können.

11.06.2013 - 16:55 Uhr

Auf die Idee mit der Suche bin ich natürlich selbst gekommen, deswegen habe ich ja an mir gezweifelt, dass du mit so einfachen Suchbegriffen einen Treffer hattest, welchen ich dann wohl übersehen hatte.

Und die Kritik mit der Google-Antwort galt nicht dir im speziellen, sondern ist meine Meinung. Wenn ich in einem Forum nach Informationen oder Hilfe suche, möchte ich ungern auf google und co verwiesen werden - dass sollte vor dem stellen der Frage erledigt sein.

Ist nicht negativ aufgenommen - sondern positiv. Mein Problem kam schlicht daher, dass ich ein bestehendes Modul von Insert auf BulkCopy umgestellt habe. Dabei hatte ich nur die das BulkCopy eingefügt, ohne die Datenstruktur zu verändern und bekam ein anderes Ergebnis. Da es vorher funktioniert hatte, vermutete ich den Fehler in meiner Verwendung von BulkCopy und nicht in der unangetasteten Datenstruktur.

Zu deiner Korrektur: float -> decimal war die Ausgangssituation. Die im letzten Post nachgereichten Kombinationen waren die Varianten, welche ich aufgrund deines Hinweises ausporbiert hatte.

Das vollständige Ergebnis ist also:

CLR     -> SQL     = Ergebnis
-----------------------------------------------------------------------------------
float   -> DECIMAL = funktioniert nicht (Verhalten im Startpost beschrieben)
double  -> DECIMAL = funktioniert nicht (Verhalten im Startpost beschrieben)
float   -> FLOAT   = funktioniert nicht (Präzision, z.B.: 1,0000000412)
double  -> FLOAT   = funktioniert (?) (Verhalten wie bei float/FLOAT denkbar, trat im Testcase jedoch nicht auf)
decimal -> DECIMAL = funktioniert
10.06.2013 - 17:35 Uhr

Dein Tipp war richtig.

Fazit:

CLR     -> SQL     = Ergebnis
float   -> FLOAT   = funktioniert nicht (Präzision)
double  -> FLOAT   = funktioniert
decimal -> DECIMAL = funktioniert

Des weiteren sehe ich gerade, dass ein Mod den Titel meines Threads angepasst hat - eine Anmerkung im Start-Post dazu wäre schön gewesen - dann wäre die Idee mit dem Order By vermutlich nicht aufgekommen. Zugegeben, der ursprüngliche Titel 'Sql2012 + BulkCopy = Kopfschmerzen' war ziemlich kreativ - aber mir viel keine kurze, das Problem zusammenfassende Beschreibung ein...

Da mich deine Äußerung dazu gebracht hat, an meinen Suchmaschienen-Fähigkeiten zu zweifeln, bin ich dem gerade mal nachgegangen... In den ersten 20 Items ist mein Problem nur einmal vertreten (ohne Lösung, kannte ich aber bereits) und eine Site nicht erreichbar (net-ado), danach hab ich nicht mehr weitergesucht. Statt meines Problemes fand ich diverse StackTrace-Logs, Exceptions, HowTo BulkCopy und Probleme mit dem DateTimeFormat -> mindestens 20min für nix.

Ich habe durchaus Verständnis dafür, das du/ihr die Nutzer anleiten wollt, selbst aktiv zu werden - was ich euch bei der ein oder anderen Fragestellung hier nicht übel nehmen kann. Jedoch solltet ihr besser differenzieren - die wenigsten nutzten das Forum ohne Eigeninititive bzw. als intelligente Suchmaschiene. Eine niedrige Posting-Anzahl hat nichts mit Faulheit oder fehlender Initiative zu tun.
In meinen Augen kann ein Verweis auf google (oder eine andere Suchmaschiene) als Antwort auf eine gestellte Frage eines von zwei Dingen sein: a) ein Wink mit dem Zaunpfahl zur Eigeninitiative - dann erwarte ich jedoch sinnvolle Ergebnisse auf der ersten Seite oder b) fehl am Platz sein

09.06.2013 - 20:16 Uhr

Weiß zwar nicht, wo ich jetzt 'patzig' geworden bin - aber sorry, falls das so aufgefasst werden konnte.

Werde deinen Lösungsvorschlag morgen ausprobieren 😃

Falls es tatsächlich daran liegen sollte, stellt sich mir aber wieder die Frage, wieso nur das BulkCopy fehlschlägt, die Inserts mit den selben Daten (der angegebenen DataTable) aber funktionieren...

09.06.2013 - 13:35 Uhr

Es geht aber nicht um die Reihenfolge der Werte.

Ich habe lediglich mehrere Werte verwendet, um den Fehler darzustellen. Konkret geht es darum, dass wenn ich mittels **SqlBulkCopy **den Wert 29.9 in die Datenbank einfüge, der Wert 29.8 in der Datenbank gespeichert wird!

Und bei BulkCopy habe ich auf die Inserts keinen Einfluss. Den einzigen Einfluss den ich habe, ist der Datentyp der Spalte in der DataTable - und der ist korrekt - und wird auch korrekt angezeigt, wenn ich das ganze im Debugger oder einem DataGridView betrachte!

08.06.2013 - 18:45 Uhr

Wo steht denn was von order by?
Das mit der Reihenfolger stimmt natürlich, ist für die vereinfachte Darstellung des Problems aber nicht relevant.

Eine Datenbank sollte die Werte wieder zurückliefern, die durch das Programm in diese geschrieben wurden - und das kann sie in diesem konkreten Fall nicht, da es weder die 29.9 noch die 29.3 gibt (Siehe Beispiel)!

In dem tatsächlichen Szenario wird natürlich eine ID verwendet 😉

07.06.2013 - 16:12 Uhr

Die Prepared-Statements könnten auch noch aus einem anderen Grund gut gehen: Da man ja Precision/Scale angeben muss, könnte es sein, dass die übergebenen Werte automatisch angepasst werden um den Einschränkungen zu genügen. Text wird ja einfach abgeschnitten, wenn er zu lang ist...

Hatte mein Szenario vorher mit Inserts umgesetzt. Dauert fast 10min. Mit BulkCopy, Triggern und Co. noch nicht mal 2. Von daher würde ich gerne bei BulkCopy bleiben...

Kann gut sein, dass bei BulkCopy andere Routinen genutzt werden, wenn diese aber Performance über Präzision stellen (würden), könnte man imho jede Datenbank danach in die Tonne treten 😐

Außerdem verwendet ja der SSMS-Daten-Assistent mit Sicherheit auch BulkCopy (oder das entsprechende CMD-Tool) beim kopieren/verschieben von Tabellen. Denn da dauert das verschieben/kopieren der befüllten Tabelle ähnlich lang - wird aber ohne Fehler durchgeführt.

07.06.2013 - 15:42 Uhr

Habs zwar nicht mit 9.7 probiert, sondern mit 5.2 (weitere Versuche kann ich erst am Montag folgen lassen). Eines der Ergebnisse war dann 29.89f - was deine These stützen würde...

Dagegen spricht meiner Meinung nach folgendes:

  • Der Debugger zeigt die Variablen korrekt an
  • Wenn ich aus der gleichen Variable Prepared-Insert-Statements generiere, ist alles ok
07.06.2013 - 14:46 Uhr

Ich habe ein ein komisches Problem bei der Verwendung von SqlBulkCopy. Um dem Problem auf den Grund zu gehen, habe ich folgendes Testcase gebastelt. Auf usings hab ich der Übersicht wegen verzichtet.

var table = new DataTable();
table.Columns.Add("Test", typeof(float));
table.Rows.Add(29.9f);
table.Rows.Add(29.7f);
table.Rows.Add(29.5f);
table.Rows.Add(29.3f);
table.Rows.Add(29.1f);

var sql = new SqlConnection();
sql.ConnectionString = '...';
sql.Open();
sql.ChangeDatabase("...");

sql.Execute("CREATE TABLE T_TEST ( Test DECIMAL(4,1) );");

var bulk = new SqlBulkCopy(sql);
bulk.DestinationTableName = "T_TEST";
bulk.WriteToServer(table);

sql.Close();

Ich hoffe, ich liege nicht ganz falsch, wenn ich erwarte, die oberen 5 Zahlen genau so auch in der Datenbank vorzufinden...? Wenn ich mir den Inhalt der DataTable im Debugger ansehe, ist auch (noch) alles in Ordnung. Wenn ich mir den Tabelleninhalt der Datenbank anzeigen lasse, wird folgendes ausgegeben:

29.8
29.7
29.5
29.2
29.1

Erwarte ich das falsche oder läuft hier irgendwas schief...?

17.05.2013 - 13:09 Uhr

Ich bin mir nicht mal sicher, ob dir das irgendwie hilft, da es eigentlich für die Dateisystem-Umleitungen verantwortlich ist...
Aber da ich für die Registry nichts vergleichbares gefunden habe - vielleicht funktioniert es ja auch da - ein Versuch kann nicht schaden...

MSDN:
Wow64DisableWow64FsRedirection function
Wow64RevertWow64FsRedirection function

PInvoke: (die entsprechenden .NET Signaturen mit Beispiel-Code)
wow64disablewow64fsredirection (kernel32)
Wow64RevertWow64FsRedirection (kernel32)

16.05.2013 - 20:10 Uhr

Ich denke nicht, dass man den .NET Dialog für unbehandelte Ausnahmen unterdrücken kann... Außerdem: Wie sicher kann ein Programm noch laufen, wenn eine unbehandelte Exception aufgetreten ist?

AppDomain.CurrentDomain.UnhandledException += ...
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += ...

Ich verwende die beiden obigen Events nur zum loggen und zeige dann einen Dialog mit Vorgehensweisen (Logdatei übermitteln, ...) und beende danach das Programm.

16.05.2013 - 19:39 Uhr

Vielleicht was in der Richtung?

public T GetValueAs<T>(string key)
{
    if(!_table.ContainsKey(key))
        throw new ArgumentException("key");

    return (T)Convert.ChangeType(_table[key], typeof(T));
    //return (T)Convert.ChangeType(_table[key], typeof(T), new CultureInfo("en-US"));
}

_table ist dein Dictionary...

var _int = GetValueAs<int>("test1");
var _string = GetValueAs<string>("test2");
var _double = GetValueAs<double>("test3");

Beim umwandeln in Kommazahlen kann es sein, dass du deine Spracheinstellungen (CultureInfo) anpassen musst, da in Deutschland als Dezimaltrennzeichen "," - meist jedoch "." verwendet wird. Dazu kannst du an ChangeType als drittes Argument eine CultureInfo Instanz übergeben.

Fehlerbehebung für nicht konvertierbare Werte solltest du auch nicht vergessen...

16.05.2013 - 19:16 Uhr

Im angesprochenen ersten Punkt hast du natürlich recht - hab ich schlicht und einfach vergessen 🤔

Das selber draufkommen ist so eine Sache... Vor allem in unbekannten Gebieten - und das ist die TPL sowie die BlockingCollection für mich... Und wer sucht denn schon, wenn er einen DeadLock produziert hat, im Sourcecode des Frameworks nach dem Fehler (oder einer falsch interpretierten Funktionsweise)?

Wie auch immer: Danke für eure begleitende Unterstützung 👍

15.05.2013 - 18:39 Uhr

Ja, ich konnte mein Problem lösen - zumindest konnte ich bisher keinen Lock mehr produzieren, unabhängig von der Job- und Worker Anzahl.

Auch mit Ursache und Lösung liegt du goldrichtig. Das Problem gibts nur bis .NET 4.0 und ist MS bekannt, ab .NET 4.5 gibt es einen internen Workaround: EnumerablePartitionerOptions.NoBuffering (siehe Antworten im Blog). Meine aktuell eingesetzte Lösung stammt ebenfalls von MS: in den ParallelExtensionExtras ist ein Partitioner enthalten, der das beschriebene Lock-Problem umgeht...

Quellen:
BlockingCollection.GetConsumingEnumerable and Parallel.ForEach hang
Lösung:
ParallelExtensionsExtras Tour - #4 - BlockingCollectionExtensions

Schade, dass du nicht früher laut über die Ursache nachgedacht hast, mit dem Hinweis wäre ich vermutlich deutlich schneller zu dem entscheidenden Hinweis gekommen^^

10.05.2013 - 11:50 Uhr

@Abt

Interlocked ist mir ein Begriff, darauf habe ich jedoch verzichtet, weil mir der Vergleichsaufruf

Interlocked.CompareExchange(ref m_jobCounter, 0, 0) == 0

ein Dorn im Auge ist. Habe es aber dennoch ausprobiert - leider das gleiche Ergebnis.
Volatile habe ich zwar schonmal gehört und gelesen (Threading in C#, Albahari), aber nachdem, was ich gelesen habe, versuche ich es zu vermeiden, da volatile CPU abhängig ist (laut dem genannten Dokument). Habe es dennoch versucht, was keinen Unterschied gemacht hat. Des weiteren warnt ReSharper, dass volatile bei der Übergabe einer Variablen als ref verloren geht...

@herbivore

Was ein Deadlock ist, ist mir bekannt. Ob es tatsächlich einer ist, weiß ich nicht - habe es nur so genannt, weil mir kein anderer Ausdruck dafür einfällt. Häufig kann man in Visual Studio, wenn ein DeadLock aufgetreten ist, die Ausführung pausieren und dann anschliessend in den existenten Threads deren Ausführungsposition sehen - das ist in meinem Fall jedoch nicht mehr möglich:*8 Threads *4 mit dem Status nicht verfügbar *1 vshost.RunParkingWindow *1 .NET SystemEvents *1 wartet auf die Rückkehr der TestCase Funktion *1 wartet in dem Testcase beim Aufruf von Parallel.ForEach

Außerdem sind bei mir 2 Fakten aktiv, die meiner Meinung nach gegen einen Deadlock sprechen:*Keiner der Aufrufe innerhalb der lock-Statements blockiert - es dürfte also höchstens zu Verzögerungen kommen - nicht jedoch zu einem Deadlock *MaxDegreeOfParallelism ist 1, also nur ein WorkerThread, was auch anhand der Debugging-Ausgabe der ManagedThreadId sichtbar wird. SOmit werden die lock-Blöcke immer nur vom gleichen Thread betreten

Edit-1:
Wenn ich den Parameter an Parallel.ForEach von GetConsumingEnumarble durch ToArray() ersetzte, kommt es in den ersten 1000-Testcase durchläufen nicht zum Lock - danach habe ich das Testcase abgebrochen.

Edit-2: Wenn ich vor Beginn des Testcase CompleteAdding aufrufe, läuft das Testcase auch fehlerfrei. Hat der Partioner vielleicht ein Problem mit dem noch ausstehenden CompleteAdding?

09.05.2013 - 13:26 Uhr

Zugegeben: Das mit dem inkrementieren VOR dem Add in dem anderen Thread ist mir irgendwie entgangen 🙁

Aber das war dafür ja jetzt unmissverständlich 😄. Also gesagt & getan. Aber es hat sich leider nicht viel geändert - es gibt immer noch einen DeadLock - jedoch kann ich den DeadLock nicht (mehr) erkennen, da die entsprechenden Threads (sofern es welche gibt) wartend oder systemeigen sind...

Aber der Reihe nach. Der Code


        public void AddJob(Job job)
        {
            lock (m_sync)
                m_jobCounter += 1;

            job.Status = JobStatus.Queued;
            m_jobs.Add(job);
        }

        private void LoopFunction(Job job, ParallelLoopState state, long index)
        {
            JobParam param = null;

            try
            {
                lock (m_sync)
                {
                    m_running.AddLast(job);

                    param = m_parameters.Dequeue();
                    param.State = state;
                    param.Index = index;
                }
                //Debug.WriteLine(Thread.CurrentThread.ManagedThreadId + " running Job " + job.Description);
                job.SafeExecute(param);
            }
            finally
            {
                lock (m_sync)
                {
                    m_parameters.Enqueue(param);
                    m_running.Remove(job);
                    m_jobCounter -= 1;

                    if (m_jobCounter == 0 && AutoClose)
                        m_jobs.CompleteAdding();
                }
            }
        }

*Der Testcase enthält 25 Jobs welche keine ChildJobs erzeugen und WorkerCount ist auf 1 gesetzt. Jeder Job gibt einen ihm zugewiesenen Buchstaben des Alphabets in die DebugConsole aus. Die Buchstaben werden fortlaufend von A an vergeben. *Der DeadLock trat bisher bei allen Versuchen (mehr als 25) reproduzierbar nach dem Buchstaben U auf - d.h.: es wurden 21 Jobs verarbeitet - auch der U Job wurde abgeschlossen. *Nachdem die obige LoopFunction nach dem U-Job verlassen wurde ist m_running.Count 0, m_jobCounter = 4 und m_jobs.Count = 4. Die LoopFunction wird aber danach nicht mehr aufgerufen...? *Der aufrufende Thread blockiert am Aufruf von Parallel.ForEach.

Und mal so zwischendruch: Danke für eure (hoffentlich weiterhin andauernde) Hilfe 🙂

08.05.2013 - 18:09 Uhr

Ja, da habe ich verstanden und auch angewandt, siehe obiges Beispiel... das leider nicht funktioniert.

Beim Eintritt in die Funktion der ForEach-Schleife wird der aktuelle Job in eine Liste geschoben, vor verlassen wieder aus der Liste entfernt. Zum Abschluss wird noch geprüft, ob es noch anstehende oder laufende Jobs gibt und dann CompleteAdding aufgerufen. So habe ich dich zumindest in dem anderen Thread verstanden - ob ich mir jetzt die Job-Instanzen merke oder einen Zähler benutze, ist ja egal.

Aber es funktioniert aus oben beschriebenen Gründen nicht - habe leider keine Ahnung, wie ich das implementieren könnte, damit die Locks die Funktionalität nicht aufheben...

07.05.2013 - 20:03 Uhr

Zum einen brauche ich Parallel.ForEach aufgrund des GetConsumingEnumerable und zum zweiten benutze ich die TPL ja - die Funktion ist die an den ForEach-Aufruf übergebene Funktionsinstanz...

07.05.2013 - 18:01 Uhr

Nachdem ich es ausprobiert habe, hat sich die obige Frage erledigt: Wenn nicht CompleteAdding aufgerufen wird/wurde, blockiert ForEach. Auch der andere Thread war nun sehr hilfreich 🙂 Aber schon die nächste Frage - folgende Funktion wird durch die ForEach aufgerufen:

        private void LoopFunction(Job job, ParallelLoopState state, long index)
        {
            JobParam param = null;

            try
            {
                lock (m_sync)
                {
                    m_running.AddLast(job); // laufenden Jobs

                    param = m_parameters.Dequeue(); // Argument an den Job
                    param.State = state;
                    param.Index = index;
                }
                job.SafeExecute(param);
            }
            finally
            {
                lock (m_sync)
                {
                    m_parameters.Enqueue(param);
                    m_running.Remove(job);

                    if (m_running.Count == 0 && Jobs.Count == 0 && AutoClose)
                        Jobs.CompleteAdding();
                }
            }
        }

Nun kommt es im Testcase (Job ist eine Funktion, welche eine Random-Instanz erzeugt, einen Double generiert und wenn dieser Wert größer als 0.5 ist, einen weiteren Job erzeugt - jedoch kann dieser keine weiteren Jobs erzeugen) immer mal wieder vor, dass ich eine InvalidOperationException erhalte - weil es zu einem Add kommt, nachdem CompleteAdding aufgerufen wurde...

Ich verstehe jetzt sogar warum, sehe aber irgendwie keine Lösung. Zu dem warum: Während Thread x die CompleteAdding Bedingung erreicht und erfüllt, wartet y VOR dem ersten lock-Statement. Der x-Thread ruft CompleteAdding auf und verlässt den Monitor. Nun kommt der y-Thread weiter und versucht, einen Job hinzuzufügen... -> InvalidOperationException.

Ein Wink mit dem Zaunpfahl in die richtige Richtig wäre toll 🤔

06.05.2013 - 16:57 Uhr

Danke für den Tipp 😃

Nun ist mir beim testen meiner Variante noch ein Denkfehler aufgefallen: Es werden zwar in jedem Zyklus n-Tasks gestartet, im Worst-Case ist zum Zeitpunkt des Aufräumens aber nur einer fertig - d.h.: es sind (n-1)-Slots belegt... Die anderen werden daher erst im nächsten Zyklus freigegeben und neu befüllt... Daher ist, zumindest in meinem Fall, die Laufzeit egal ob mit 1, 2, 4, 8 oder 16 - Threads (4 CPU + HT) nahezu identisch...

Das dürfte bei der Parallel.ForEach Variante ja nicht der Fall sein, oder? Da sollte unmittelbar nach der Rückkehr des Workers ein neuer oder der gleiche wieder ins Rennen geschickt werden...? Werd mir mal ein entsprechendes Testcase basteln...

Edit:
Was passiert eigentlich bei der ForEach-Variante für den unwahrscheinlichen Fall, dass die BlockingCollection leer wird, aber noch Tasks am laufen sind? Wird dann noch auf die Tasks gewartet, da diese ja weitere Jobs hinzufügen könnten oder ist dann sofort Schluss?

04.05.2013 - 14:02 Uhr

@herbivore: natürlich muss es nicht die TPL sein, ich dachte nur, dass diese mir vielleicht etwas Arbeit abnehmen kann. Ein bischen Neugier auf die TPL war auch dabei 8o

Mein Ansatz sieht nun so aus:


var cts = new CancellationTokenSource();
var ct = cts.Token;
var jobs = new BlockingCollection<string>();
var running = new List<Task>();

while (true)
{
    lock (jobs)
    {
        if (jobs.Count > 0)
        {
            // Task erstellen und starten
            var task = Task.Factory.StartNew(obj =>
                {
                    var localList = (BlockingCollection<string>)obj;
                    var job = localList.Take(ct);

                    lock (localList)
                        localList.Add("test");
                }, jobs, ct);

            // Task-Referenz merken
            running.Add(task);
        }
    }

    // Wenn n-Tasks am laufen sind, auf einen Abschluss warten
    if (running.Count == Environment.ProcessorCount)
        Task.WaitAny(running.ToArray(), ct);

    // Abgeschlossene Tasks entfernen
    running.RemoveAll(x => x.IsCanceled || x.IsCompleted || x.IsFaulted);

    // Wenn es keine laufenden Tasks und keine Jobs mehr gibt
    lock (jobs)
        if (running.Count == 0 && jobs.Count == 0) break;
}

Ich hoffe das while(true) sowie die ganzen 'var' seien mir verziehen - ist außerdem nicht getestet. Ist die Synchronisation auf jobs erforderlich/ausreichend?

Gerade noch ein bischen gegoogelt... Ist das wirklich SO EINFACH:?


var jobs = new BlockingCollection<string>();

Parallel.ForEach(jobs.GetConsumingEnumerable(), (obj) =>
{
    var job = (string)obj;
    jobs.Add("test");
});

Im letzten Fall wird aber vermutlich das Element nach der Verarbeitung nicht entfernt, oder? Gibt es eine Möglichkeit, das umzusetzten? Take liefert ja das erste Element und bei Enumerable ist die Position nicht definiert... Und wie finde ich in dieser Variant heraus, wann der Vorgang beendet (d.h.: keine laufenden Tasks und leere Job-Liste) ist? Habe ja hier keinen Zugriff auf die laufenden Tasks...

04.05.2013 - 12:32 Uhr

Ja und Nein. Ich drücke mich wahrscheinlich wieder kompliziert oder undeutlich aus 🤔

BlockingCollection ist aber ein guter Hinweis, hatte noch eine alte eigene JobQueue aus .NET 2.0 verwendet...

Aber die eigentliche Frage ist, ob (und wenn ja wie) es die TPL ermöglicht, eine solche Collection mit mit n-Workern gleichzeitig zu verarbeiten. D.h.: immer n-Worker, wenn einer fertig ist einen neuen zu starten usw, OHNE dass ich die Verwaltungsmethoden selber implementieren muss - also quasi das, wass Parallel.For/ForEach mit einer unveränderlichen Collection macht.

04.05.2013 - 10:49 Uhr

Hallo,

ich habe folgendes Szenario:

Eine Liste/Queue mit Jobs. Diese Liste möchte ich parallel verarbeiten. Während der Verarbeitung eines Jobs kann dieser neue Jobs erzeugen. Nach der Verarbeitung eines Jobs muss dieser aus der Liste/Queue entfernt werden.*Old-School (eigene Threads, eigene Queue mit Shutdown-Flag, Scheduler der den Vorgang beendet, wenn alle Worker warten, ...) ist das ja ein schon ein wenig Aufwand. *Aber wie sieht das mit der TPL (.NET 4.0) aus? Parallel.For/ForEach geht meiner Meinung nach nicht, da sich die Liste ja laufend ändert. Ist es möglich, Tasks so zu verketten/erzeugen, dass immer n-Worker die Task-Liste verarbeiten?

Code wäre überirdisch 8), Pseudo-Code und allgemeine Infos zur möglichen Umsetzung würden aber vollkommen reichen 😁

02.04.2012 - 17:21 Uhr

Das Stichwort für Bilder ist "XMP". Wird aber leider nur für JPEG und TIFF Bilder unterstützt und ist auch erst durch die WPF direkt in .NET verfügbar... werd mir also was anderes ausdenken müssen...

02.04.2012 - 16:10 Uhr

ja, klingt danach, als würden wir das gleiche meinen. Mir ist bewusst, dass sich einige die Mehrzahl von Dateien gar nicht auf diese Art bewerten lässt...
Mir geht es jedoch hauptsächlich um Bilder, welche aus einer Art Slideshow heraus mittels Tastendruck (1-5) bewertet werden sollen - und diese Bewertung soll auch im Windows Explorer sichtbar sein (und auch von dort verändert werden können).

Eine Idee, wie ich auf diese Dateieigenschaften prüfen und selbige dann ggf. lesen oder schreiben kann?

02.04.2012 - 15:14 Uhr

Gruzi

ich suche nach einer Möglichkeit, die im Windows-Explorer getätige Dateibewertung (1-5 Sterne) lesen und auch schreiben zu können.
.NET direkt wirds wahrscheinlich eng, vermutlich WinAPI... Aber auch da haben mich google und co nicht weitergebracht, da die suche nach "file rating" zumeist Artikel und ähnliches aufstöbert, die Dateien behandeln und die man im Anschluss bewerten kann (CodeProject & co.).

Hat vielleicht jemand ne Idee wie ich Zugriff auf die Dateibewertung erhalte oder wonach ich konkreter suchen könnte?

Danke 😃

18.01.2012 - 19:35 Uhr

Hallo,

ich habe eine MDI Anwendung, welche diverse Plugins aus einem Verzeichnis lädt. Vereinfacht dargestellt sieht ein Plugin so aus:


interface Plugin
{
  // Control kann alles sein, vom Label bis zu einem komplexen Formular
  Control Feedback {get;}
  void Init();
  void Run();
}

Die MDI-Anwendung startet, je nach Konfigurationsdatei ein einzelnes Plugin oder mehrere Plugins gleichzeitig.

Für jedes zu startende Plugin wird ein eigener Thread erstellt, in welchem die Plugin-Funktionen ausgeführt werden. Für eine Plugin-Instanz wird natürlich immer der gleiche Thread verwendet. Als erstes wird Init von der MDI-Main aufgerufen - Init erstellt die Formulare (falls vorhanden) und bereitet das Plugin auf die Ausführung vor. In Init wird auch das Feedback-Control erstellt (sofern vorhanden).

Der Gedanke war nun, bevor die MDI-Main nun Run aufruft, dass Feedback-Control in ein Formular zu packen und dieses als Child hinzuzufügen - Was natürlich nicht funktioniert 🙁 Hab leider den genauen Fehlertext nicht im Kopf, aber es geht darum, dass ein Steuerelement, welches in Thread A erstellt wurde, nicht in Thread B einem Formular hinzugefügt werden kann. Versteh ich soweit auch - geht in die gleiche Richtung, wie dass ein Steuerelement nicht von einem Thread verändert werden kann, in dem es nicht erstellt wurde.

Gibt es eine Lösung für das skizzierte Problem, welche unabhängig vom verwendeten Control ist? Wie funktioniert das z.B.: bei Google-Chrome, wo die Tabs in eigenen Threads ausgeführt werden? Sind das nur Worker, die die Arbeit erledigen und die erhaltenen Daten an das UI zurück posten?

Danke schonmal für eure Ideen und Vorschläge 🙂

15.04.2011 - 19:56 Uhr

Servus - wie schon angedroht, Ende der Woche, da bin ich wieder 😃

Hab mir mal alles durchgelesen - und mich anschliessend mit der IFolderView Variante etwas genauer befasst.
Dr. Google hat mir mal wieder gezeigt, dass viele die gleiche oder ähnliche Frage haben, aber kaum einer die passenden Antworten... Nach ein bischen rumprobieren habe ich folgende Funktion geschrieben:


//
// GUIDs, Shell Interfaces, Native Methods, ...
//

public static void X(string dir, FOLDERVIEWMODE viewMode)
{
	IntPtr ptrMalloc = IntPtr.Zero;
	IntPtr pidlFolder = IntPtr.Zero;
	IntPtr ptrParent = IntPtr.Zero;
	IntPtr ptrRelative = IntPtr.Zero;
	IntPtr ptrFolderView = IntPtr.Zero;
	IntPtr ptrShellView = IntPtr.Zero;
	IMalloc iMalloc = null;
	IShellView iShellView = null;
	IShellFolder iShellFolder = null;
	IFolderView iFolderView = null;
	uint attribute = 0;
	int hrResult = 0;

	try
	{
		hrResult = Shell32.SHGetMalloc(out ptrMalloc);
		if (!SUCCEEDED(hrResult))
			return;

		iMalloc = (IMalloc)Marshal.GetTypedObjectForIUnknown(ptrMalloc, typeof(IMalloc));

		hrResult = Shell32.SHParseDisplayName(dir, IntPtr.Zero, out pidlFolder, 0, out attribute);
		if (!SUCCEEDED(hrResult))
			return;

		hrResult = Shell32.SHBindToParent(pidlFolder, IID_IShellFolder, out ptrParent, ref ptrRelative);
		if (!SUCCEEDED(hrResult))
			return;

		iShellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(ptrParent, typeof(IShellFolder));

		hrResult = iShellFolder.CreateViewObject(IntPtr.Zero, ref IID_IShellView, out ptrShellView);
		if (!SUCCEEDED(hrResult))
			return;

		iShellView = (IShellView)Marshal.GetObjectForIUnknown(ptrShellView);

		hrResult = Marshal.QueryInterface(ptrShellView, ref IID_IFolderView, out ptrFolderView);
		if (!SUCCEEDED(hrResult))
			return;

		iFolderView = (IFolderView)Marshal.GetObjectForIUnknown(ptrFolderView);
		iFolderView.SetCurrentViewMode((uint)viewMode);
	}
	finally
	{
		if (pidlFolder != IntPtr.Zero)
			iMalloc.Free(pidlFolder);
		if (ptrParent != IntPtr.Zero)
			iMalloc.Free(ptrParent);
		if (ptrRelative != IntPtr.Zero)
			iMalloc.Free(ptrRelative);
		if (ptrFolderView != IntPtr.Zero)
			iMalloc.Free(ptrFolderView);
		if (ptrShellView != IntPtr.Zero)
			iMalloc.Free(ptrShellView);


		Marshal.ReleaseComObject(iShellView);
		Marshal.ReleaseComObject(iShellFolder);
		Marshal.ReleaseComObject(iFolderView);
		Marshal.ReleaseComObject(iMalloc);
	}
}

Diese läuft bei Aufruf zwar fehlerfrei durch, im angegebenen Verzeichnis tut sich jedoch gar nix 😦
Jemand eine Idee, wo der "Hase im Pfeffer" liegt?

Werde mir jetzt mal die Explorer-Variante ansehen, würde diese aber gern vermeiden, da ich diese Funktion in einem Batch-Vorgang brauche - und in einer Batch-Verarbeitung immer wieder neue Prozesse zu erstellen gefällt mir nicht so ganz...

10.04.2011 - 21:40 Uhr

Danke 😁 - sieht schonmal sehr viel versprechend aus...
werde mich aber leider erst Ende nächster Woche wieder dazu äußern können, ob es mich weiter gebracht hat...

EDIT:
Leider zu früh gefreut - so weit war ich auch schon. Bin daran gescheitert, dass ich nicht herausfinden konnte, zu welchem Verzeichnis dann beispielsweise der Eintrag gehört:

HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\Bags\xxxx
10.04.2011 - 18:37 Uhr

Hallo,

wie der Titel schon verrät, suche ich nach einer Möglichkeit, programmgesteuert (C#) den Windows Explorer anzuweisen, für ein bestimmtes Verzeichnis eine bestimmte Ansicht ("Extra große Symbole") zu verwenden.

Dr. Google hat mir leider nicht wirklich weitergeholfen, sondern nur die Möglichkeit aufgezeigt, wie ich Registry-Basierend die Standardansicht (also für alle Verzeichnisse) ändern kann...
Habe auch schon mit ProcMon (Sysinternals) die Registry-Änderungen vor und nach der Änderung über den WindowsExplorer überwacht, werde aber aus den aufgezeigten Änderungen nicht schlau...

Ein "manueller" Eingriff in die Registry wäre für mich die letzte Lösung. Dafür gibts bestimmt ne WinAPI Funktion, habe jedoch keine Ahnung, wonach ich suchen soll 🤔

Ziel-OS: Eigentlich alle, Entwicklungsmaschiene ist Win7

Das ganze in Pseudo-Code:


enum View { ExtraBigSymbols, BigSymbols, MediumSymbols, SmallSymbols, List, Details, Content }
void SetView(string targetDir, View view);

13.11.2010 - 13:38 Uhr

*push* 🙂

niemand eine Idee?

07.11.2010 - 17:14 Uhr

warum bin ich da nicht selbst drauf gekommen?

...alle Projekte, die ich gefunden habe, funktionieren in umgekehrter Richtung. Die meisten wollen den SixAxis Controler am PC verwenden, nicht aber den PC als SixAxis Controler an der Playstation 3...

07.11.2010 - 16:49 Uhr

Servus,

ich suche nach einer Möglichkeit, einen PC an einer Playstation 3 als Controler anzumelden und diese dann zu steuern.
Kennt jemand ein entsprechendes Projekt oder vielleicht eine Bibliothek, die mir eine entsprechende API zur Verfügung stellt?

19.06.2010 - 12:13 Uhr

Für eine Dienstinstallation geht das auf jeden Fall viaServiceInstaller.StartType. Die Parameter kann man da aber nicht direkt ändern, sondern muss den Dienst erst deinstallieren und dann mit den neuen Einstellungen wieder installieren...

Vielleicht hilft dir ja ChangeServiceConfig, da ist weiter unten auch ein Beispiel... Ist dann allerdings kein Managed-Code mehr 😉

18.06.2010 - 15:33 Uhr

Nach etlichen google-Sessions habe ich mein Problem - zumindest bis jetzt - lösen können.

Seit dem ich die Namen der Ausgabeassembly's so angegeben habe, dass er den jeweiligen Standardnamespace enthält ([Standardnamespace].[exe|dll]) ist der Fehler bei mir nicht mehr aufgetreten...

17.06.2010 - 18:34 Uhr

sch... schade.

ja, kann dem neuen Hilfesystem auch noch nichts positives abgewinnen... kommt aber vielleicht noch 😉

VS2008 hab ich auch noch, kann man die kontextsensitive Hilfe ("F1") von 2010 denn an den alten DocumentExplorer koppeln?

16.06.2010 - 23:24 Uhr

Leider nicht ganz... Es ja nicht darum, die On- oder Offline Hilfe umzuschalten, sondern das statt eines zusätzlichen Fensters (Firefox) alles in der IDE bleibt, was mir die Navigation erheblich erleichtern würde...

16.06.2010 - 21:35 Uhr

Servus,

Mein Standardbrowser ist Firefox. Ich suche nach einer Einstellungsmöglichkeit, die beim drücken von F1 die Hilfe als Tab in der IDE öffnet, statt auf den Standardbrowser zurückzugreifen.

Jemand ne Idee?

16.06.2010 - 21:30 Uhr

Servus,

ja, das gleiche Problem hab ich mit VS2010 ebenfalls.
Projekt enthält eine WinForms Anwendung, eine eigene Klassenbibliothek sowie zwei 3rd Party Bibliotheken.

Falls du das Problem zwischenzeitlich lösen konntest, wäre ich für den Lösungsweg dankbar 😃