Long running in-process Publish/Subscribe class I have a Pub/Sub class that will call Notify() when published and that Notify() could take around 10 seconds, so I need to make sure that this does not block the UI.

public static class NotificationService
{
    private static Dictionary&lt;string, INotification&gt; Observers { get; }

    static NotificationService()
    {
        Observers = new Dictionary&lt;string, INotification&gt;();
    }

    public static void Publish(string title, string requester)
    {
        foreach (var observer in Observers)
        {
            var notification = observer.Value;

            new Thread(() =&gt;
            {
                Thread.CurrentThread.IsBackground = true;
                notification.Notify(title, requester);
            }).Start();

        }
    }

    public static void Subscribe(INotification notification)
    {
        INotification notificationValue;
        if (Observers.TryGetValue(notification.NotificationName, out notificationValue))
        {
            // Observer already exists
            return;
        }

        Observers[notification.NotificationName] = notification;
    }

    public static void UnSubscribe(INotification notification)
    {
        INotification notificationValue;
        if (!Observers.TryGetValue(notification.NotificationName, out notificationValue))
        {
            // Observer doesn't exists
            return;
        }
        Observers.Remove(notification.NotificationName);
    }
}


INotification

public interface INotification
{
    string NotificationName { get; }

    bool Notify(string title, string requester);
}


Example implementation of INotification 

public class EmailMessageNotification : INotification
{
    public EmailMessageNotification(EmailNotificationSettingsService settings)
    {
        EmailNotificationSettings = settings;
    }

    private EmailNotificationSettingsService EmailNotificationSettings { get; }
    public string NotificationName =&gt; "EmailMessageNotification";
    public bool Notify(string title, string requester)
    {
        var configuration = GetConfiguration();
        if (!ValidateConfiguration(configuration))
        {
            return false;
        }

        var message = new MailMessage
        {
            IsBodyHtml = true,
            To = { new MailAddress(configuration.RecipientEmail) },
            Body = $"User {requester} has requested {title}!",
            From = new MailAddress(configuration.EmailUsername),
            Subject = $"New Request for {title}!"
        };

        try
        {
            using (var smtp = new SmtpClient(configuration.EmailHost, configuration.EmailPort))
            {
                smtp.Credentials = new NetworkCredential(configuration.EmailUsername, configuration.EmailPassword);
                smtp.EnableSsl = configuration.Ssl;
                smtp.Send(message);
                return true;
            }
        }
        catch (SmtpException smtp)
        {
            Log.Fatal(smtp);
        }
        catch (Exception e)
        {
            Log.Fatal(e);
        }
        return false;
    }

    private EmailNotificationSettings GetConfiguration()
    {
        // Gets and Returns Config
    }

    private bool ValidateConfiguration(EmailNotificationSettings settings)
    {
        // Do some validation
    }


Can anyone see anything wrong with this?
It seems that you have a global static logger methods. It is not a good practice, as you're loosing the possibility to tune logging levels for different areas of your application (e.g. setting log level for EmailMessageNotification to DEBUG while keeping INFO level everywhere else. I recommend to follow the logging patterns suggested for your logging framework (see Creating loggers for NLog for example).
The code is not thread-safe, so you may get issues if your application publishes, subscribes or unsubscribes in different threads. Use either locking around Observers collection or ‎ConcurrentDictionary.
Fatal logging level should be used only when the application encounters a critical error after which it usually cannot continue to run. In your case I would suggest to use Error log level when email cannot be sent.
NotificationService is static, which means that you will have a strong dependencies in your code on this class. I suggest to introduce IoC framework (check out Autofac or StructureMap).
The response of INotification.Notify(string, string) is not used, so it can be void.
Your current code requires spinning up threads per each observer per each notification. I suggest to switch to asynchronous processing model, changing the return type of INotification.Notify(string, string) to Task.
Most likely you would want to wrap the notification of each listener in try..catch to avoid side effects when buggy INotification subscribes to your service


As a result:

INotification:

public interface INotification
{
    string NotificationName { get; }

    Task NotifyAsync(string title, string requester);
}


EmailMessageNotification:

public class EmailMessageNotification : INotification
{
    private static Logger logger = LogManager.GetCurrentClassLogger();

    public EmailMessageNotification(EmailNotificationSettingsService settings)
    {
        EmailNotificationSettings = settings;
    }

    private EmailNotificationSettingsService EmailNotificationSettings { get; }

    public string NotificationName =&gt; "EmailMessageNotification";

    public async Task NotifyAsync(string title, string requester)
    {
        var configuration = GetConfiguration();
        if (!ValidateConfiguration(configuration))
        {
            return;
        }

        var message = new MailMessage
        {
            IsBodyHtml = true,
            To = { new MailAddress(configuration.RecipientEmail) },
            Body = $"User {requester} has requested {title}!",
            From = new MailAddress(configuration.EmailUsername),
            Subject = $"New Request for {title}!"
        };

        try
        {
            using (var smtp = new SmtpClient(configuration.EmailHost, configuration.EmailPort))
            {
                smtp.Credentials = new NetworkCredential(configuration.EmailUsername, configuration.EmailPassword);
                smtp.EnableSsl = configuration.Ssl;
                await smtp.SendMailAsync(message).ConfigureAwait(false);
            }
        }
        catch (SmtpException smtp)
        {
            logger.Error(smtp);
        }
        catch (Exception e)
        {
            logger.Error(e);
        }
    }

    private EmailNotificationSettings GetConfiguration()
    {
        throw new NotImplementedException();
    }

    private bool ValidateConfiguration(EmailNotificationSettings settings)
    {
        throw new NotImplementedException();
    }
}


NotificationService:

public interface INotificationService
{
    void Publish(string title, string requester);
    void Subscribe(INotification notification);
    void UnSubscribe(INotification notification);
}

public class NotificationService : INotificationService
{
    private static Logger logger = LogManager.GetCurrentClassLogger();
    private ConcurrentDictionary&lt;string, INotification&gt; Observers { get; } = new ConcurrentDictionary&lt;string, INotification&gt;();

    private static async Task NotifyAsync(INotification notification, string title, string requester)
    {
        try
        {
            await notification.NotifyAsync(title, requester).ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            logger.Error(ex, $"Notification '{notification.NotificationName}' failed with exception");
        }
    }

    public async void Publish(string title, string requester)
    {
        IEnumerable&lt;Task&gt; notificationTasks = Observers.Values.Select(notification =&gt; NotifyAsync(notification, title, requester));

        await Task.WhenAll(notificationTasks).ConfigureAwait(false);
    }

    public void Subscribe(INotification notification)
    {
        Observers.TryAdd(notification.NotificationName, notification);
    }

    public void UnSubscribe(INotification notification)
    {
        Observers.TryRemove(notification.NotificationName, out notification);
    }
}