C# Language Deadlocks (deux threads en attente sur l'autre)


Exemple

Une impasse est ce qui se produit lorsque deux ou plusieurs threads attendent que chacun se termine ou libère une ressource de telle manière qu'ils attendent pour toujours.

Un scénario typique de deux threads en attente d'exécution l'un de l'autre est lorsqu'un thread d'interface graphique Windows Forms attend un thread de travail et le thread de travail tente d'appeler un objet géré par le thread d'interface graphique. Observez qu'avec ce code, cliquer sur button1 provoquera le blocage du programme.

private void button1_Click(object sender, EventArgs e)
{
    Thread workerthread= new Thread(dowork);
    workerthread.Start();
    workerthread.Join();
    // Do something after
}

private void dowork()
{
    // Do something before
    textBox1.Invoke(new Action(() => textBox1.Text = "Some Text"));
    // Do something after
}

workerthread.Join() est un appel qui bloque le thread d'appel jusqu'à la fin de workerthread. textBox1.Invoke(invoke_delegate) est un appel qui bloque le thread d'appel jusqu'à ce que le thread GUI ait traité invoke_delegate, mais cet appel provoque des blocages si le thread d'interface graphique attend déjà la fin du thread appelant.

Pour contourner ce problème, il est possible d'utiliser un moyen non bloquant d'appeler la zone de texte à la place:

private void dowork()
{
    // Do work
    textBox1.BeginInvoke(new Action(() => textBox1.Text = "Some Text"));
    // Do work that is not dependent on textBox1 being updated first
}

Toutefois, cela entraînera des problèmes si vous devez exécuter du code qui dépend de la zone de texte mise à jour en premier. Dans ce cas, exécutez cela dans le cadre de l'appel, mais sachez que cela le fera fonctionner sur le thread d'interface graphique.

private void dowork()
{
    // Do work
    textBox1.BeginInvoke(new Action(() => {
        textBox1.Text = "Some Text";
        // Do work dependent on textBox1 being updated first, 
        // start another worker thread or raise an event
    }));
    // Do work that is not dependent on textBox1 being updated first
}

Vous pouvez également démarrer un tout nouveau thread et laisser celui-ci faire l'attente sur le thread d'interface graphique, afin que workerthread puisse se terminer.

private void dowork()
{
    // Do work
    Thread workerthread2 = new Thread(() =>
    {
        textBox1.Invoke(new Action(() => textBox1.Text = "Some Text"));
        // Do work dependent on textBox1 being updated first, 
        // start another worker thread or raise an event
    });
    workerthread2.Start();
    // Do work that is not dependent on textBox1 being updated first
}

Pour minimiser le risque de tomber dans une impasse d’attente mutuelle, évitez toujours les références circulaires entre les threads lorsque cela est possible. Une hiérarchie de threads où les threads de moindre rang ne laissent de messages que pour des threads de rang supérieur et ne les attendent jamais ne se heurteront pas à ce type de problème. Cependant, il serait toujours vulnérable aux blocages basés sur le verrouillage des ressources.