So. Wenn du das "ihr Süßen" weglässt (was mich auch immer leicht nervt), dann will ich auch versuchen nett zu sein (oder zumindest sachlich :)).
Zum Thema.
Die Boost.ASIO garantiert dir, dass Completion-Handler nur aus Threads aufgerufen werden, die grade io_service::run() ausführen. Ohne io_service::run() könnte es also auch keine Garantie dieser Art geben. Irgendeinen Thread braucht man ja um Code auszuführen, und wenn man nicht vom User einem Bekommt, dann müsste man (die ASIO) selbst welche erzeugen, und der User könnte diese Threads nichtmehr "kontrollieren". Und eine andere (portable, unproblematische) Art, einer Library einen Thread zu "borgen", kenne ich nicht.
Der Sinn der Garantie wiederum ist, ... also da gibt es ein paar Dinge die mir einfallen:
Für bestimmte Dinge muss man Threads sozusagen "vorbereiten", damit man sie machen kann. Zumindest bei manchen Betriebssystemen. Auch gibt es bestimmte Dinge (Daten, Zustände - wie man auch sagen will), die an einen Thread gekoppelt sind.
Beispiel: wenn du in Windows COM verwenden willst, musst du den Thread in ein COM-Apartment tun, was man mit CoInitialize() bzw. CoInitializeEx() macht. Es kann beliebig viele "Single Threaded Apartments" (STA - eines pro Thread), und genau ein "Single Threaded Apartment" (MTA) pro Prozess geben. Und alle COM Objekte "gehören" ihrem Apartment, d.h. du darfst sie nicht aus einem Thread der zu einem anderen Apartment gehört verwenden.
Wenn du nun in einem Completion-Handler COM Objekte verwenden willst, und nicht kontrollieren kannst in welchem Thread der Completion-Handler ausgeführt wird, dann wäre das sehr lästig.
So aber kannst du den Thread einfach vor dem io_service::run() Aufruf entsprechend "vorbereiten", was eine feine Sache ist.
Genau das, also dass man Threads nicht "vorbereiten" kann, ist auch einer meiner Kritikpunkte am .NET Thread-Pool. Sehr lästig. (Ich muss allerdings zugeben, dass ich mich nicht zu wetten trauen würde, dass es nicht doch irgendeine Möglichkeit gibt, die ich bloss übersehen habe. Aber ich schweife ab.)
Du kannst dir das Threading-Modell dadurch selbst aussuchen, und relativ flexibel gestalten. Du kannst von Single-Threaded bis Multi-Threaded mit dynamischer Thread-Anzahl so ziemlich alles machen. Du kannst sogar mehrere io_service Objekte mit komplett getrennten Thread-Pools verwenden. Oder auch nur einem Thread pro io_service Objekt.
Wenn kein Thread gerade io_service::run() ausführt, ist auch garantiert, dass keine Completion-Handler ausgeführt werden. Das klingt jetzt vielleicht nicht nach einem tollen Nutzen, aber ich bin ziemlich sicher, dass man Anwendungsfälle findet, wo es parktisch ist.
io_service::run() ermöglicht dir, zentral an einer Stelle Exceptions zu fangen, die von Completion-Handlern geworfen werden. Die ASIO garantiert dir nämlich, dass Exceptions die in Completion-Handlern geworfen werden, einfach unverändert aus io_service::run() rausfliegen. Dort kannst du sie dann z.B. fangen, nen Eintrag ins Logfile schreiben, und gleich wieder io_service::run() aufrufen. Oder was auch immer als Reaktion auf die Exception Sinn macht. IMO auch eine *sehr* nette Sache, die das sinnvolle Arbeiten mit ASIO und Exceptions überhaupt erst möglich macht.
Eine denkbare Alternative wäre hier gewesen, dass man dem User die Möglichkeit gibt, irgendwie einen Exception-Handler zu definieren, der aufgerufen wird, wenn einer seiner Completion-Handler eine Exception wirft. Die Variante über io_service::run() finde ich allerdings 100x eleganter.