2010-03-03 3 views
30

Je sais qu'il existe d'autres messages qui indiquent que vous pouvez créer un contrôle, puis vérifier la propriété InvokeRequired pour voir si le thread actuel est le thread principal ou non.Comment savoir si un thread est le thread principal en C#

Le problème est que vous n'avez aucun moyen de savoir si ce contrôle lui-même a été créé sur le thread principal.

J'utilise le code suivant pour savoir si un fil est le fil conducteur (le fil qui a commencé le processus):

if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA || 
    Thread.CurrentThread.ManagedThreadId != 1 || 
    Thread.CurrentThread.IsBackground || Thread.CurrentThread.IsThreadPoolThread) 
{ 
    // not the main thread 
} 

Est-ce que quelqu'un sait une meilleure façon? Il semble que cette façon de procéder pourrait être sujette à des erreurs ou casser des versions futures de l'exécution.

+1

Que voulez-vous dire par «fil principal»? Et qu'est-ce que vous en avez besoin? –

+0

Le thread sur lequel "public static void Main" est lancé.J'ai besoin de savoir parce que j'utilise C++/COM interop et le code sous-jacent exige qu'il soit invoqué uniquement à partir du thread à partir duquel il a été initialisé (le thread principal dans mon cas) – jjxtra

Répondre

36

Vous pouvez le faire comme ceci:

// Do this when you start your application 
static int mainThreadId; 

// In Main method: 
mainThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; 

// If called in the non main thread, will return false; 
public static bool IsMainThread 
{ 
    get { return System.Threading.Thread.CurrentThread.ManagedThreadId == mainThreadId; } 
} 

EDIT Je vous réalisé pourrait le faire avec la réflexion aussi, voici un extrait pour que:

public static void CheckForMainThread() 
{ 
    if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA && 
     !Thread.CurrentThread.IsBackground && !Thread.CurrentThread.IsThreadPoolThread && Thread.CurrentThread.IsAlive) 
    { 
     MethodInfo correctEntryMethod = Assembly.GetEntryAssembly().EntryPoint; 
     StackTrace trace = new StackTrace(); 
     StackFrame[] frames = trace.GetFrames(); 
     for (int i = frames.Length - 1; i >= 0; i--) 
     { 
      MethodBase method = frames[i].GetMethod(); 
      if (correctEntryMethod == method) 
      { 
       return; 
      } 
     } 
    } 

    // throw exception, the current thread is not the main thread... 
} 
+0

On dirait que le meilleur coup pour moi. –

+0

Tant que vous garantissez que cette statique est initialisée à partir du thread principal cela fonctionnerait. Et si cela ne peut pas être garanti? Que faire si la classe est d'abord instanciée à partir d'un autre thread? – jjxtra

+1

@Jeff: Vous devriez le faire explicitement dans votre méthode d'entrée, ou cela ne fonctionne pas. Personnellement, je ne l'initialiserais pas en ligne, mais je le ferais directement dans votre routine Main(), car alors vous avez la garantie qu'il est géré correctement, et c'est plus évident. Cela étant dit, à moins que vous ne manipuliez plusieurs pompes de messages (ce qui me semble être une mauvaise pratique), l'utilisation de SynchronizationContext fonctionnera parfaitement. –

16

Si vous utilisez Windows Forms ou WPF, vous pouvez vérifier si SynchronizationContext.Current n'est pas null.

Le thread principal obtiendra un jeu SynchronizationContext valide dans le contexte actuel au démarrage dans Windows Forms et WPF.

+1

+1 - Suppression de la mine –

+0

Et si vous démarrez un MessagePump (Application.Run()) sur un deuxième thread? –

+0

@Henk: Personnellement, je ne recommande pas de le faire, en général, mais cela pourrait potentiellement gâcher si vous le faites, ET si vous utilisez ce thread pour vérifier si c'est le fil "principal" ... Dans cette situation, La réponse de Henk est à peu près la seule option, mais c'est celle que je n'aime pas particulièrement. –

3

Essayez celui-ci if (Application.MessageLoop). Je pense qu'il est le cas rare d'avoir plus de 1 fil pour traiter les messages

2

Dans mon expérience, si vous essayez de créer une boîte de dialogue d'un autre thread autre que le thread principal puis fenêtres obtient tout confus et les choses commencent à devenir fou. J'ai essayé de le faire une fois avec une fenêtre d'état pour montrer l'état du thread d'arrière-plan (et bien d'autres fois quelqu'un lancerait une boîte de dialogue à partir d'un thread d'arrière-plan - et une boucle Message Loop) faire des choses "aléatoires" dans le programme. Je suis à peu près sûr qu'il y a eu une manipulation dangereuse de quelque chose qui se passe. J'ai eu des problèmes en cliquant sur le formulaire et le mauvais fil de discussion des messages ...

Donc, je n'aurais jamais une interface utilisateur provenant de partout, mais le fil principal. Cependant, pourquoi ne pas enregistrer simplement le CurrentThread lorsque vous démarrez et comparer le ThreadID avec le thread actuel?

-Chert

+1

Pour ajouter, après que l'ordinateur se réveille à partir de la veille, le flux d'exécution de n'importe quel thread d'interface utilisateur dans l'application d'interface utilisateur multithread reprend sur n'importe quel thread d'interface utilisateur (pas nécessairement sur le même où il était avant le sommeil). – miroxlav

10

Voici une autre option:

if (App.Current.Dispatcher.Thread == System.Threading.Thread.CurrentThread) 
{ 
    //we're on the main thread 
} 

Works pour moi.

EDIT: Oublié de mentionner que cela ne fonctionne que dans WPF. Je cherchais SO pour le cas WPF, et je n'ai pas remarqué que cette question est générale. NET. Une autre option pour Windows Forms pourrait être

if (Application.OpenForms[0].InvokeRequired) 
{ 
    //we're on the main thread 
} 

Bien sûr, vous devez d'abord vous assurer qu'il y ait au moins un Form dans l'application.

+0

De quels espaces de noms provient-il? Est-il disponible dans les applications de la console et des formulaires Windows? Qu'en est-il de WPF? – jjxtra

Questions connexes