Meus dois centavos.
Primeiro, a maioria dos modelos assíncronos que vi nas bibliotecas tendem a me deixar frustrado. Todo mundo parece ter sua própria marca um pouco diferente de assíncrono, e muitas dessas interfaces não são boas. Por isso, gosto de bibliotecas que mantêm tudo sincronizado. Observe que os retornos de chamada ainda podem ser bons. Mas mantenha toda a lógica em um thread; o programador de aplicativos geralmente quer pensar em segmentar além da tarefa que a biblioteca está tentando executar.
Em segundo lugar, uma pequena biblioteca dedicada ao código assíncrono pode ser uma coisa muito boa - desde que esse seja seu único foco. Um padrão que vi e gostei em C # é encadear ações em diferentes encadeamentos, mas escrevê-lo de maneira quase que única, com uma interface fluente. (A nova palavra-chave aguardar é um pouco ao longo das mesmas linhas.) Um lugar comum em que isso ocorre é em despachos para o thread da interface do usuário. Em seguida, forneça uma maneira de lidar com exceções no final, quase como um bloco catch. Então, por exemplo, talvez algo assim:...
int expensiveResult=-1;
YourThreadLibrary
.Background(()=>expensiveResult=DoLongRunningTaskToCreate())
.UI(()=>UpdateUI(expensiveResult))
.Exception(ex=>LogIt(ex));
Exceções são o caminho a percorrer em C # e você tem GC, então sua situação pode ser diferente. Mas o padrão ainda pode fazer algum sentido.
Eu sei que isso pode parecer muito simplista, mas essas são as ferramentas que eu vi serem genéricas o suficiente para trabalhar em vários problemas. O bom é que uma interface fluente como essa é definitivamente aberta à extensão se escrita corretamente para que você possa adicionar seu .ParallelFailOnAny (params Action []) etc.