0

in order to make UI responsive to User i started to use Multitasking in my Wpf application.

However its confusing for me.

i have seen too much ways of multitasking and now im completely confused what should i do.

BackgroundWorker seemed ugly way to me so i used Task.Factory.StartNew

my problem is that i cant connect other threads to Main Thread.

Exception : The calling thread cannot access this object because a different thread owns it

i have Global variable that is

private static AssetDeclaration _assetDeclaration = new AssetDeclaration();

this variable is set inside LoadFile through lock statement.

First code i wrote did not work. _assetDeclaration Remains Empty.

            Task.Factory.StartNew(() =>
            {
                Parallel.ForEach(opd.FileNames, LoadFile);//read multiple files together and set values to asset one by one using lock
            });
            FillTreeView();
            LoadAssets();

then i decided to put two other methods in thread.

            Task.Factory.StartNew(() =>
            {
                Parallel.ForEach(opd.FileNames, LoadFile);
                Dispatcher.Invoke(FillTreeView);
                LoadAssets();
            });

now the _assetDeclaration value set correctly.also two other methods works correctly

but Later i cant write ViewPort3D.Children.Add(_modelVisual3Ds[i]); because _modelVisual3Ds values are set in another thread (in LoadAssets Method) and cant be used in MainThread. also i tried Dispatcher.Invoke but does not work.

also _modelVisual3Ds members are Freezable. so i freeze them inside thread but they cant be used in Mainthread again.

so can someone explain what is happening?

Edit: i have added more code. to describe. the code is simplified because i cant paste 600 lines of code !

Open file Button:

    private void MenuItem_Open(object sender, RoutedEventArgs e)
    {
        // here user chooses some files to open. and we start opening it!

        //...

        if (opd.ShowDialog() == true)
        {
            Task.Factory.StartNew(() =>
            {
                Parallel.ForEach(opd.FileNames, LoadFile);
                Dispatcher.Invoke(FillTreeView);
                LoadAssets();
            });
        }
    }

Global Variables:

    private static readonly object LockThread = new object(); // to lock

    private static AssetDeclaration _assetDeclaration = new AssetDeclaration(); // Class that holds needed files
    private static List<ModelVisual3D> _modelVisual3Ds = new List<ModelVisual3D>(); // one of _modelVisual3Ds member must be added to viewport3D children

LoadAssets:

    private static void LoadAssets()
    {
        //...
        // i try to freeze each content but they remain not accessible in Main Thread
        foreach (ModelVisual3D modelVisual3D in _modelVisual3Ds)
        {
            modelVisual3D.Content.Freeze();
        }
    }

Later on loading model in to Viewport3D

ViewPort3D.Children.Add(_modelVisual3Ds[i]);// exception thrown here

"This API was accessed with arguments from the wrong context."

Jongware
  • 22,200
  • 8
  • 54
  • 100
M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
  • You need simple objects for file contents multithreaded reading. For example you can load contents to memory streams then create needed objects from these buffers in main thread. You can use events or other synchronization methods for this. – Onur Jun 16 '15 at 17:16
  • im really new at multithreading. can you provide some links or explain it more simple? thanks @Onur – M.kazem Akhgary Jun 16 '15 at 17:23
  • Do you mind posting a more complete code example? Unfortunately, it's hard to get the complete picture with the code fragments you posted. And the problem with that, is that many times, the problem is hidden where you least expect it, so having more context is very helpful! EDIT: If you have too much code to post, then the ideal would be for you to write up a simplified version of your program that still reproduces your problem, and post that instead. – sstan Jun 16 '15 at 17:24
  • Can you post loadfile method? It seems that in your case you should create object in main thread because code checks to prevent thread mixing – Onur Jun 16 '15 at 17:27
  • yes wait a moment im writing it. @sstan – M.kazem Akhgary Jun 16 '15 at 17:30
  • If you are reading files then doing it in parallel is not going to be faster unless they are on separate physical disks. On the other end is a single write heads. Let it do one file then move to the next. – paparazzo Jun 16 '15 at 17:40
  • i tested the results by timer and using parallel to readfiles are 3 times faster in my computer (single harddisk) @Blam – M.kazem Akhgary Jun 16 '15 at 17:42
  • Load assets needs to be out of thread method. You can wait for thread to finish. Then call load assets. – Onur Jun 16 '15 at 17:45
  • 1
    Please do not use `static` objects for `lock`ing. It might lead to deadlocks. Also, avoid `static` fields in general, this is a poor design decision. – dymanoid Jun 16 '15 at 17:47
  • I am not buying that. It may be faster because you are doing more than just read. A write head is a physical device that can only read one at a time. The fastest way to read is serial file by file. Arbitrary parallel is not the correct path to making you app faster or scalable. – paparazzo Jun 16 '15 at 17:52
  • after i come out from Task.Factory.StartNew everything clear out. because of static? – M.kazem Akhgary Jun 16 '15 at 17:56
  • @Blam: concurrent I/O operations _can_ improve performance: first, if each thread is doing more than just I/O i.e. processing the data, using multithreaded code allows processing to be done concurrently with the I/O; second, up to a point issuing concurrent I/O operations allows the OS and the storage device to optimize access, reducing the number of seeks required for a given set of data being read. In any case, throughput does not seem to be the basis for this question, so it would be better to stay focused on the actual question than to distract with tangential comments. – Peter Duniho Jun 16 '15 at 19:12
  • 1
    The canonical answer to the cross-thread exception you are getting is to either use `Dispatcher.Invoke()` or frozen `Freezable` objects. Unfortunately, you are not specific about in which way those two options "don't work". There are a number of existing questions on SO addressing the general issue; if you have a more specific question than those, please provide [a good, _minimal_, _complete_ code example](http://stackoverflow.com/help/mcve) and a clear explanation of what _specifically_ you are having trouble getting to work. – Peter Duniho Jun 16 '15 at 19:14
  • i also get this error report i forgot. `This API was accessed with arguments from the wrong context.` from where i want to attach model to viewport. @PeterDuniho – M.kazem Akhgary Jun 16 '15 at 20:02
  • _"im having it in different situation"_ -- as a programmer, an important skill for you to learn is to be able to generalize from other situations. Just because the duplicate questions (there are many) don't have _exactly_ the same code as you, that doesn't mean your problem isn't the same. In any case, as I said: if you believe your problem is unique, you need to present your question in a way that makes that clear, including providing a proper code example (see the link in my previous comment). – Peter Duniho Jun 16 '15 at 20:09
  • agreed. sorry for that. however im working on it. but i think i explained as best as i could but ill try to make it more clear. also see the edit in the last part if i use `Dispatcher.Invoke` ill get that exception @PeterDuniho – M.kazem Akhgary Jun 16 '15 at 20:14
  • The best way to make it clear (to yourself and to the rest of us) is for you to create a proper code example. Very often, the act of doing so gives you yourself the context you need to understand and fix the issue. But if it doesn't, you'll at least have a good code example to use in presenting your question. The code you've posted here is really both at the same time too much (i.e. includes parts not relevant to the question) and not enough (is not a complete, compilable, runnable example), and fails to usefully illustrate what you're having trouble with. – Peter Duniho Jun 16 '15 at 20:19
  • @PeterDuniho Tangential? The question is about speeding up the UI. If the thread is doing more than just IO then it is not optimized. Parallel IO can lead to some real scale issues and far from from a tangential issue. – paparazzo Jun 16 '15 at 20:52
  • @Blam: no. The question is about accessing objects cross-thread. It is in the _context_ of making the UI _responsive_ (which is not the same anyway as improving the _performance_ of the UI), and in any case the context isn't what the question is _about_. It's just the scenario in which the question exists. – Peter Duniho Jun 16 '15 at 20:58
  • @PeterDuniho You are not the question police. You may focus on the symptoms and I focus on the problem. I do exactly that kind of optimization in a production application. – paparazzo Jun 16 '15 at 21:01
  • _"You are not the question police"_ -- you are correct about that. If I were, I would have issued you a citation or arrested you. Instead, I am simply pointing out the errors in your statements, and encouraging you to not distract from the actual question. You are free to ignore this helpful advice to your heart's content. – Peter Duniho Jun 16 '15 at 21:28
  • if i use `Dispatcher.Invoke(LoadAssets);` in thread it will work and program runs. i guess i fixed the problem ? @PeterDuniho – M.kazem Akhgary Jun 16 '15 at 22:08
  • You wrote in your question _"i tried Dispatcher.Invoke but does not work"_. Now you write that `Dispatcher.Invoke()` does work. There's not enough information here for me to know what the difference is, why it didn't work before, etc. never mind know whether you fixed the problem. But I hope that you did. If you have more problems, please post a new question and use the guidelines I've offered above to ensure it's a well-presented, answerable question. For future reference, see also http://stackoverflow.com/help/how-to-ask for additional advice. – Peter Duniho Jun 16 '15 at 22:12
  • Thanks. that time i used `Dispatcher.Invoke(()=> ViewPort3D.Children.Add(_modelVisual3Ds[i]));` , and it did nothing because it was already in main thread. now i find out that i must use dispatcher inside thread. thanks for all cares. and sorry for asking Disturbed question. im really sorry. ill see the link to write better questions in future @PeterDuniho – M.kazem Akhgary Jun 16 '15 at 22:25

0 Answers0