Когда Б обрабатывает событие, возникает исключение как будто метод класса Б работает в потоке объекта А, это нормально?
Ну вы же инвокнули ивент в другом потоке, с чего бы ему автоматически искать другой, "нужный" поток? Вы сами должны знать, в каком потоке вам поднимать событие. Если оно внутреннее, то может и в рабочем потоке правильно его генерировать, а если событие видно извне (т.е. можно подписаться извне класса), а сам класс А абстрагирует существование и работу некоего другого потока (т.е. клиенты НЕ должны об этом знать) - то тогда логично самому заботиться о передаче ивента в нужный поток. Помните, что именно вы определяете поточную архитектуру приложения, и вам решать какой код в рамках каких потоков будет отрабатывать.
Пофиксил через SynchronizationContext, но может быть есть получше варианты?
Нормальный вариант, если вы используете подсистему, у которой есть реализация SynchronizationContext, например WinForms или WPF. Если же это не ваш вариант, то нужно предоставить свою реализацию, или же отказаться от его использования и явно использовать очереди сообщений для общения между потоками.