Модульный тест на безопасность поток?

Очень хорошая grep замена для GVim Ack. Поисковый плагин, записанный в Perl, который бьет внутреннюю grep реализацию Vim и внешне вызванные власти, также. Это также пропусками по умолчанию любые каталоги CVS в каталоге проекта, например, '.svn'. Этот блог показывает способ интегрировать Ack с энергией.

66
задан TheSean 11 November 2009 в 15:13
поделиться

7 ответов

Есть два продукта, которые могут вам в этом помочь:

Оба проверяют наличие взаимоблокировок в вашем коде (с помощью модульного теста) и я думаю Chess проверяет также для гоночных условий.

Использовать оба инструмента легко - вы пишете простой модульный тест, запускаете свой код несколько раз и проверяете, возможны ли в вашем коде тупиковые ситуации / условия гонки.

Изменить: Google выпустил инструмент, который проверяет состояние гонки во время выполнения (не во время тестов), который называется thread-race-test .
он не найдет все условия гонки, потому что он анализирует только текущий запуск, а не все возможные сценарии, как инструмент выше, но он может помочь вам найти условие гонки, когда это произойдет.

Обновление: На сайте Typemock больше не было ссылки на Racer, и он не обновлялся последние 4 года. Думаю, проект закрыли.

21
ответ дан 24 November 2019 в 15:08
поделиться

Проблема в том, что большинство проблем многопоточности, таких как состояния гонки, недетерминированы по своей природе. Они могут зависеть от поведения оборудования, которое вы не можете эмулировать или запускать.

Это означает, что даже если вы проводите тесты с несколькими потоками, они не будут постоянно давать сбой, если в вашем коде есть дефект.

10
ответ дан 24 November 2019 в 15:08
поделиться

Я видел, как люди пытались проверить это с помощью стандартных модульных тестов, как вы сами предлагаете. Тесты проходят медленно, и до сих пор не удалось выявить ни одной из проблем параллелизма, с которыми борется наша компания.

После множества неудач, несмотря на мою любовь к модульным тестам, я пришел к выводу, что ошибки параллелизма не относятся к числу unittests сильные стороны. Я обычно рекомендую анализ и обзор в пользу модульных тестов для классов, где параллелизм является предметом обсуждения. С полным обзором системы во многих случаях можно доказать / опровергнуть утверждения о безопасности потоков.

В любом случае я хотел бы, чтобы кто-нибудь дал мне что-то, что может указывать на обратное, поэтому я внимательно слежу за этим вопросом.

3
ответ дан 24 November 2019 в 15:08
поделиться

testNG или Junit с тестовым модулем springframeworks (или другим расширением) имеют базовую поддержку для тестирования параллелизма.

Эта ссылка может вас заинтересовать

http://www.cs.rice.edu/~javaplt/papers/pppj2009.pdf

1
ответ дан 24 November 2019 в 15:08
поделиться

вам нужно будет создать тестовый пример для каждого интересующего сценария параллелизма; это может потребовать замены эффективных операций более медленными эквивалентами (или имитаторами) и выполнения нескольких тестов в циклах, чтобы увеличить вероятность разногласий

без конкретных тестовых примеров, трудно предложить конкретные тесты

, некоторые потенциально полезные справочные материалы:

1
ответ дан 24 November 2019 в 15:08
поделиться

Когда мне недавно пришлось решать ту же проблему, я думал об этом так; Прежде всего, у вашего существующего класса есть одна обязанность - обеспечить некоторую функциональность. Обеспечение потоковой безопасности не входит в обязанности объектов. Если он должен быть потокобезопасным, для обеспечения этой функциональности следует использовать какой-либо другой объект. Но если какой-то другой объект обеспечивает потокобезопасность, он не может быть необязательным, потому что тогда вы не можете доказать, что ваш код потокобезопасен. Вот как я с этим справляюсь:

// This interface is optional, but is probably a good idea.
public interface ImportantFacade
{
    void ImportantMethodThatMustBeThreadSafe();
}

// This class provides the thread safe-ness (see usage below).
public class ImportantTransaction : IDisposable
{
    public ImportantFacade Facade { get; private set; }
    private readonly Lock _lock;

    public ImportantTransaction(ImportantFacade facade, Lock aLock)
    {
        Facade = facade;
        _lock = aLock;
        _lock.Lock();
    }

    public void Dispose()
    {
        _lock.Unlock();
    }
}

// I create a lock interface to be able to fake locks in my tests.
public interface Lock
{
    void Lock();
    void Unlock();
}

// This is the implementation I want in my production code for Lock.
public class LockWithMutex : Lock
{
    private Mutex _mutex;

    public LockWithMutex()
    {
        _mutex = new Mutex(false);
    }

    public void Lock()
    {
        _mutex.WaitOne();
    }

    public void Unlock()
    {
        _mutex.ReleaseMutex();
    }
}

// This is the transaction provider. This one should replace all your
// instances of ImportantImplementation in your code today.
public class ImportantProvider<T> where T:Lock,new()
{
    private ImportantFacade _facade;
    private Lock _lock;

    public ImportantProvider(ImportantFacade facade)
    {
        _facade = facade;
        _lock = new T();
    }

    public ImportantTransaction CreateTransaction()
    {
        return new ImportantTransaction(_facade, _lock);
    }
}

// This is your old class.
internal class ImportantImplementation : ImportantFacade
{
    public void ImportantMethodThatMustBeThreadSafe()
    {
        // Do things
    }
}

Использование дженериков позволяет использовать фальшивую блокировку в ваших тестах, чтобы убедиться, что блокировка всегда берется при создании транзакции и не снимается до тех пор, пока транзакция не будет удалена. Теперь вы также можете убедиться, что блокировка была снята при вызове важного метода. Использование в производственном коде должно выглядеть примерно так:

// Make sure this is the only way to create ImportantImplementation.
// Consider making ImportantImplementation an internal class of the provider.
ImportantProvider<LockWithMutex> provider = 
    new ImportantProvider<LockWithMutex>(new ImportantImplementation());

// Create a transaction that will be disposed when no longer used.
using (ImportantTransaction transaction = provider.CreateTransaction())
{
    // Access your object thread safe.
    transaction.Facade.ImportantMethodThatMustBeThreadSafe();
}

Убедившись, что ImportantImplementation не может быть создан кем-либо другим (например, создав его в провайдере и сделав его частным классом), вы теперь можете доказать, что ваш класс является потокобезопасным, поскольку к нему нельзя получить доступ без транзакции и транзакции всегда принимает блокировку при создании и снимает ее при удалении.

Убедитесь, что транзакция размещена правильно, может быть сложнее, и в противном случае вы можете увидеть странное поведение в своем приложении. Вы можете использовать такие инструменты, как Microsoft Chess (как предлагается в другом ансамбле), чтобы искать такие вещи. Или вы можете попросить вашего провайдера реализовать фасад и заставить его реализовать его следующим образом:

    public void ImportantMethodThatMustBeThreadSafe()
    {
        using (ImportantTransaction transaction = CreateTransaction())
        {
            transaction.Facade.ImportantMethodThatMustBeThreadSafe();
        }
    }

Несмотря на то, что это реализация, я надеюсь, вы сможете выяснить тесты для проверки этих классов по мере необходимости.

Убедиться, что транзакция размещена правильно, может быть сложнее, и в противном случае вы можете увидеть странное поведение в своем приложении. Вы можете использовать такие инструменты, как Microsoft Chess (как предлагается в другом ансамбле), чтобы искать такие вещи. Или вы можете попросить вашего провайдера реализовать фасад и заставить его реализовать его следующим образом:

    public void ImportantMethodThatMustBeThreadSafe()
    {
        using (ImportantTransaction transaction = CreateTransaction())
        {
            transaction.Facade.ImportantMethodThatMustBeThreadSafe();
        }
    }

Несмотря на то, что это реализация, я надеюсь, вы сможете выяснить тесты для проверки этих классов по мере необходимости.

Убедиться, что транзакция размещена правильно, может быть сложнее, и в противном случае вы можете увидеть странное поведение в своем приложении. Вы можете использовать такие инструменты, как Microsoft Chess (как предложено в другом ансамбле), чтобы искать подобные вещи. Или вы можете попросить вашего провайдера реализовать фасад и заставить его реализовать его следующим образом:

    public void ImportantMethodThatMustBeThreadSafe()
    {
        using (ImportantTransaction transaction = CreateTransaction())
        {
            transaction.Facade.ImportantMethodThatMustBeThreadSafe();
        }
    }

Несмотря на то, что это реализация, я надеюсь, вы сможете выяснить тесты для проверки этих классов по мере необходимости.

2
ответ дан 24 November 2019 в 15:08
поделиться

Обратите внимание, что в ответе Дрора это явно не говорится, но, по крайней мере, Chess (и, возможно, Racer) работают, пропуская набор потоков через все их возможные чередования, чтобы получить представимые ошибки. Они не просто запускают потоки какое-то время в надежде, что если произойдет ошибка, это произойдет случайно.

Например, шахматы пройдут все чередования, а затем выдадут вам строку тега, которая представляет чередование, в котором была обнаружена тупиковая ситуация. на, чтобы вы могли приписать свои тесты конкретным чередованиям, которые интересны с точки зрения взаимоблокировки.

Я не знаю точной внутренней работы этого инструмента и того, как он отображает эти строки тегов обратно в код, который вы, возможно, изменяете чтобы исправить тупик, но вот оно ...

5
ответ дан 24 November 2019 в 15:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: