Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.60/5: Рейтинг темы: голосов - 5, средняя оценка - 4.60
2 / 2 / 2
Регистрация: 23.06.2007
Сообщений: 399

Недопустимая операция в нескольких потоках

20.09.2018, 05:50. Показов 1084. Ответов 5
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Каким образом из фонового потока передать значения в основной?
Вот такой тест вызывает ошибку "Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'label1' не из того потока, в котором он был создан"
Как правило, примеры используют методы контрола, которых изменяется, но мне это не подходит, т.к подпискиков на событие TestClass-а много и разных, контролы в этих подписчиках тоже разные, поэтому разрешить проблему надо внутри TestClass.
Почему именно внутри TestClass, а не в Form1 - хочу изменить реализацию метода Start(долго обрабатывает и все приложение тормозит), вынести в отдельный поток.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    public partial class Form1 : Form
    {
        TestClass cls = new TestClass();
 
        public Form1()
        {
            InitializeComponent();
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            cls.OnEvent += OnEvent;//подписчиков много и разных
            cls.Start();
        }
        private void OnEvent(object sender, EventArgs e)
        {
            label1.Text = (string)sender;
        }
    }
    public class TestClass
    {
        public EventHandler OnEvent;
 
        public void Start()
        {
            Thread th = new Thread(ThreadStart);
            th.IsBackground = true;
            th.Start();
        }
        private void ThreadStart()
        {
            while(true)
            {
                OnEvent?.Invoke(DateTime.Now.ToString(),null);//вот тут и затык               
                Thread.Sleep(2000);
            }
        }
    }
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
20.09.2018, 05:50
Ответы с готовыми решениями:

Недопустимая операция в нескольких потоках
В программе используется прием данных с СОМ-порта. Когда данные поступают на СОМ-порт, срабатывает прерывание и вызывается функция...

Недопустимая операция в нескольких потоках
Dictionary<string, string> accounts = new Dictionary<string, string>(); Application.DoEvents(); foreach (string...

Недопустимая операция в нескольких потоках.
Доброго времени суток. Так как в C# я новичек, мне пнадобилась Ваша помощь. Нашел пример в книге (мониторинг каталогов). Все...

5
Эксперт .NET
 Аватар для Rius
13121 / 7682 / 1675
Регистрация: 25.05.2015
Сообщений: 23,450
Записей в блоге: 14
20.09.2018, 07:18
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using System.Windows.Forms;
 
namespace WindowsFormsAppOldpasp
{
    public partial class Form1 : Form
    {
        private TestClass _cls;
 
        public Form1()
        {
            InitializeComponent();
            _cls = new TestClass();
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            _cls.Tick += OnTick;//подписчиков много и разных
            _cls.Start();
        }
 
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            _cls.Stop();
        }
 
        private void OnTick(object sender, TestClass.TickEventArgs e)
        {
            label1.Text = e.Value.ToLongTimeString();
        }
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
using System;
using System.Threading;
using System.Threading.Tasks;
 
namespace WindowsFormsAppOldpasp
{
    public class TestClass : IDisposable
    {
        private TaskScheduler _contextUI;
        private Task _task;
        private CancellationTokenSource _token;
 
        public class TickEventArgs : EventArgs
        {
            public TickEventArgs(DateTime value)
            {
                Value = value;
            }
 
            public DateTime Value { get; private set; }
        }
 
        public EventHandler<TickEventArgs> Tick;
 
        public TestClass()
        {
            _contextUI = TaskScheduler.FromCurrentSynchronizationContext();
            _task = null;
            _token = null;
        }
 
        public void Dispose()
        {
            Stop();
        }
 
        public void Start()
        {
            if (_token == null)
            {
                _token = new CancellationTokenSource();
                _task = Task.Factory.StartNew(o => Process((CancellationToken)o), _token.Token, _token.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
            }
        }
 
        public void Stop()
        {
            if (_token != null)
            {
                try
                {
                    _token.Cancel();
                    _task.Wait();
                }
                catch (AggregateException)
                {
                }
                finally
                {
                    _token = null;
                    _task = null;
                }
            }
        }
 
        private void Process(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                Task.Factory.StartNew(() => Tick?.Invoke(this, new TickEventArgs(DateTime.Now)), token, TaskCreationOptions.None, _contextUI);
                token.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
            }
        }
    }
}
1
2 / 2 / 2
Регистрация: 23.06.2007
Сообщений: 399
20.09.2018, 07:54  [ТС]
Ох ни...себе. Я, признаться, так глубоко еще не лазил. Попробую разобраться. Спасибо.
0
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
20.09.2018, 08:11
Цитата Сообщение от oldpasp Посмотреть сообщение
Каким образом из фонового потока передать значения в основной?
Как правило, примеры используют методы контрола, которых изменяется, но мне это не подходит, т.к подпискиков на событие TestClass-а много и разных, контролы в этих подписчиках тоже разные, поэтому разрешить проблему надо внутри TestClass.
Если оставаться в рамках обычных потоков, без Task, то можно использовать следующее решение:
1) Создаем класс-расширение:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    /// <summary>Extension methods for EventHandler-type delegates.</summary>
    public static class EventExtensions
    {
        /// <summary>Raises the event (on the UI thread if available).</summary>
        /// <param name="multicastDelegate">The event to raise.</param>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">An EventArgs that contains the event data.</param>
        /// <returns>The return value of the event invocation or null if none.</returns>
        public static object Raise<T>(this MulticastDelegate multicastDelegate, object sender, T e) where T : EventArgs
        {
            object retVal = null;
            var threadSafeMulticastDelegate = multicastDelegate;
 
            if (threadSafeMulticastDelegate != null)
                foreach (Delegate d in threadSafeMulticastDelegate.GetInvocationList())
                {
                    var synchronizeInvoke = d.Target as ISynchronizeInvoke;
                    if ((synchronizeInvoke != null) && synchronizeInvoke.InvokeRequired)
                        retVal = synchronizeInvoke.EndInvoke(synchronizeInvoke.BeginInvoke(d, new[] { sender, e }));
                    else
                        retVal = d.DynamicInvoke(new[] { sender, e });
                }
 
            return retVal;
        }
    }
Этот класс имеет метод Raise, который вызывает событие в обработчиках. При этом, если объект-обработчик поддерживает интерфейс ISynchronizeInvoke, то вызов метода осуществляется в контексте синхронизации. Проще говоря если подписан контрол, то его обработчик вызовется в главном GUI потоке. Если подписан НЕ контрол, то вызов произойдет в текущем потоке.

2) Ну и далее просто меняем вашу строку
C#
1
OnEvent?.Invoke(DateTime.Now.ToString(),null);//вот тут и затык
на
C#
1
OnEvent?.Raise(DateTime.Now.ToString(), EventArgs.Empty);
3
907 / 664 / 318
Регистрация: 23.10.2016
Сообщений: 1,543
20.09.2018, 10:45
Цитата Сообщение от Storm23 Посмотреть сообщение
C#
1
var threadSafeMulticastDelegate = multicastDelegate;
Лишняя переменная. Делегат - неизменяемый тип.
1
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10427 / 5157 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
20.09.2018, 14:21
Цитата Сообщение от TopLayer Посмотреть сообщение
Лишняя переменная. Делегат - неизменяемый тип.
Не, не лишняя. Он то неизменяемый, но он может быть равен null.
Может быть ситуация что уже после проверки на null другой поток отписался от события, и делегат стал null. А мы попытаемся его вызвать и получим NullReferenceException.
Подробнее см например здесь https://stackoverflow.com/ques... hread-safe

Добавлено через 9 минут
TopLayer,
Мм.. хотя я подумал, действительно лишняя, ведь мы уже зафиксировали переменную когда передали делегат в метод как значение.
Ок, вы правы, можно чуть попроще:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    /// <summary>Extension methods for EventHandler-type delegates.</summary>
    public static class EventExtensions
    {
        /// <summary>Raises the event (on the UI thread if available).</summary>
        /// <param name="multicastDelegate">The event to raise.</param>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">An EventArgs that contains the event data.</param>
        /// <returns>The return value of the event invocation or null if none.</returns>
        public static object Raise<T>(this MulticastDelegate multicastDelegate, object sender, T e) where T : EventArgs
        {
            object retVal = null;
 
            if (multicastDelegate != null)
                foreach (Delegate d in multicastDelegate.GetInvocationList())
                {
                    var synchronizeInvoke = d.Target as ISynchronizeInvoke;
                    if ((synchronizeInvoke != null) && synchronizeInvoke.InvokeRequired)
                        retVal = synchronizeInvoke.EndInvoke(synchronizeInvoke.BeginInvoke(d, new[] { sender, e }));
                    else
                        retVal = d.DynamicInvoke(new[] { sender, e });
                }
 
            return retVal;
        }
    }
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
20.09.2018, 14:21
Помогаю со студенческими работами здесь

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

Недопустимая операция в нескольких потоках
Пытаюсь создать поток таким образом: Thread thread = new Thread(check2); thread.IsBackground = true; ...

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

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

Почему первый вариант дает исключение "Недопустимая операция в нескольких потоках", а второй нет?
есть два варианта кода: Task.Run(() =&gt; { for (; ; ) { ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Новые блоги и статьи
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка SDL3, Box2D, FreeType и SDL3_ttf из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru