Há uma diferença técnica importante entre as condições da corrida e as corridas de dados. A maioria das respostas parece assumir que esses termos são equivalentes, mas não são.
Uma corrida de dados ocorre quando 2 instruções acessam o mesmo local de memória, pelo menos um desses acessos é uma gravação e não há ocorre antes da solicitação entre esses acessos. Agora, o que constitui um acontecimento antes do pedido está sujeito a muito debate, mas, em geral, os pares ulock-lock na mesma variável de bloqueio e os pares de sinal de espera na mesma variável de condição induzem uma ordem de antes do acontecimento.
Uma condição de corrida é um erro semântico. É uma falha que ocorre no tempo ou na ordem dos eventos que leva ao programa incorreto comportamento .
Muitas condições de corrida podem ser (e de fato são) causadas por corridas de dados, mas isso não é necessário. De fato, as corridas de dados e as condições de corrida não são necessárias nem suficientes para uma a outra. Esta postagem no blog também explica muito bem a diferença, com um exemplo simples de transação bancária. Aqui está outro exemplo simples que explica a diferença.
Agora que definimos a terminologia, vamos tentar responder à pergunta original.
Dado que as condições de corrida são erros semânticos, não existe uma maneira geral de detectá-las. Isso ocorre porque não há como ter um oráculo automatizado que possa distinguir o comportamento correto e incorreto do programa no caso geral. A detecção de corrida é um problema indecidível.
Por outro lado, as corridas de dados têm uma definição precisa que não se relaciona necessariamente à correção e, portanto, é possível detectá-las. Existem muitos tipos de detectores de corrida de dados (detecção de corrida de dados estática / dinâmica, detecção de corrida de dados com base em conjunto, detecção de corrida de dados baseada em fatos anteriores, detecção de corrida de dados híbrida). Um detector de corrida de dados dinâmicos de última geração é o ThreadSanitizer que funciona muito bem na prática.
O tratamento das corridas de dados em geral requer alguma disciplina de programação para induzir as ocorrências antes das arestas entre os acessos aos dados compartilhados (durante o desenvolvimento ou depois que eles são detectados usando as ferramentas mencionadas acima). isso pode ser feito por meio de bloqueios, variáveis de condição, semáforos, etc. No entanto, também é possível empregar diferentes paradigmas de programação, como passagem de mensagens (em vez de memória compartilhada), que evitam corridas de dados por construção.