Фоновый рабочий, использующий COM-объект, все еще блокирует пользовательский интерфейс

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

Итак, моя установка заключается в том, что у меня есть класс

public class Connection
{
    public static event EventHandler LogggedIn;
    public static TDConnection TDC {get;set;}
    public string Authenticate(){...}
    public static void Login()
    {
        if (Connection.TDC.Connected)
        {
            _bw = new BackgroundWorker
            {
                WorkerReportsProgress = true,
                WorkerSupportsCancellation = true
            };
            _bw.DoWork += ConnectToProject_DoWork;
            _bw.RunWorkerCompleted += ConnectToProject_RunWorkerCompleted;

            _bw.RunWorkerAsync(Connection.TDC);
        }
    }

    private static void ConnectToProject_DoWork(object o, DoWorkEventArgs e)
    {
        Connection.TDC.ConnectProjectEx(Connection.Domain, Connection.Project, Connection.UserName, Utilities.Encryption.AESEncryption.Decrypt(Connection.Password, "fsd*#(dfs(((>>>???fdjs"));
    }

    private static void ConnectToProject_RunWorkerCompleted(object o, RunWorkerCompletedEventArgs e)
    {
        LogggedIn(null, new EventArgs());
    }
}

В моем основном классе я создаю новое соединение и вызываю Login, который открывает новое соединение с ALM в TDConnection. В моем потоке я хочу использовать это уже открытое соединение внутри моего потока. Из того, что я прочитал, если я сделаю это, мой пользовательский интерфейс заблокируется, потому что я использую методы для члена в потоке пользовательского интерфейса, даже если я нахожусь внутри фонового рабочего.

Одно решение, которое я нашел для этого:

private static void ConnectToProject_DoWork(object o, DoWorkEventArgs e)
    {
        TDConnection conn = new TDConnection();
        conn.InitConnectionEx(QCURL);

        conn.Login();

        conn.ConnectProject();

        e.Result = conn;
    }

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

Я попытался передать Connection.TDC с помощью _bw.RunorkerAsync(Connection.TDC), но это тоже явно не работает.

Есть ли способ использовать уже установленное соединение и не блокировать пользовательский интерфейс во время его подключения?


person Smeiff    schedule 31.08.2012    source источник
comment
Здесь нет кода пользовательского интерфейса. Вы вызываете метод этого класса из своего потока пользовательского интерфейса, который будет блокироваться до тех пор, пока фоновый рабочий процесс не завершится?   -  person pstrjds    schedule 31.08.2012
comment
Ваши настройки WorkerReportsProgress и Cancellation вводят в заблуждение (поскольку не используются).   -  person Henk Holterman    schedule 31.08.2012
comment
Извини. Я использую Connection.Login в обработчике событий нажатия кнопки Winform. Поскольку соединение будет открываться в потоке, я подключил обработчик событий, который срабатывает при установлении соединения. Поскольку он вызывается из моей Winform, он блокирует мой пользовательский интерфейс.   -  person Smeiff    schedule 31.08.2012


Ответы (1)


Это характерно для COM-объектов. Как и классы .NET, многие компонентные классы COM не являются потокобезопасными. В .NET вы можете прострелить себе ногу, если используете класс .NET небезопасным для потоков способом. Не в COM, он гарантирует, что составной класс, который объявляет себя не ориентированным на потоки, будет использоваться безопасным для потоков способом.

Что он и делает, автоматически направляя вызов метода из рабочего потока в поток, создавший объект. Вы можете видеть, куда это идет, вы создали объект TDC в основном потоке. Поэтому, когда вы вызываете его из BackgroundWorker, он по-прежнему выполняет вызов в основном потоке.

Единственный способ решить эту проблему — создать объект в том же потоке, в котором вы его используете. Что обычно также означает, что вы не можете использовать BackgroundWorker, вам может понадобиться создать поток и вызвать его метод SetApartmentState(), чтобы переключить его на STA.

person Hans Passant    schedule 31.08.2012
comment
Вы знаете хороший пример, на который я могу посмотреть для этого? Я не слышал о SetApartmentState и, на первый взгляд, не нашел слишком много полезных статей. - person Smeiff; 31.08.2012
comment
Может быть, я что-то упускаю, но переключение на поток STA означает, что он все еще будет блокироваться, не так ли? Может быть, я не понимаю ваш пример, но если я вызову Connecton.TDC.ConnectToProject внутри потока STA, он все равно будет правильно блокироваться? - person Smeiff; 31.08.2012
comment
Конечно. Дело в том, что блокировка происходит в потоке отличном от вашего основного потока. - person Hans Passant; 31.08.2012
comment
Таким образом, если бы у меня было больше кода для выполнения после вызова потока входа в систему, он бы выполнялся во время входа в систему? Мне нужно, чтобы пользовательский интерфейс не блокировался. Я думаю, что я просто пойду со своей оригинальной работой, если нет другого пути. Спасибо за помощь! - person Smeiff; 31.08.2012