veelvoorkomende abstracte programmeerproblemen

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
leek me wel een algemeen topic waard :) (correct me if i'm wrong :P)

om te beginnen de mijne..

in mn multithreaded programma heb ik veel functies in een object die een mutex locken, iets aanpassen en daarna weer unlocken.

bijvoorbeeld:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Spatial {
  ..
  void SetPosition(const Vector3 &position) {
    dataMutex.Lock();
    this->position = position;
    dataMutex.Unlock();
  }

  void SetRotation(const Quaternion &rotation) {
    dataMutex.Lock();
    this->rotation = rotation;
    dataMutex.Unlock();
  }
  ..
};


vervolgens lekker makkelijk in het gebuik:
C++:
1
2
mijnFiets->SetPosition(blaPos);
mijnFiets->SetRotation(blaRot);


heel leuk en aardig, maar nu lock/unlock ik dezelfde mutex 2 keer, terwijl dat naar 1 keer had gehoeven. niet zo erg in dit geval, maar je snapt het performance-probleem bij uitgebreidere voorbeelden.

dus dan maar de locks buiten de class:
C++:
1
2
3
4
mijnFiets->LockMutex();
mijnFiets->SetPosition(blaPos);
mijnFiets->SetRotation(blaRot);
mijnFiets->UnlockMutex();


snel, maar error-prone.. je moet maar net weten welke functies een gelockte mutex behoeven, en het maakt het coden voor de eindgebruiker van je lib er niet makkelijker op.

WAT TE DOEN?! :o :o

kom dit probleem wel vaker tegen in andere contexten.. ben benieuwd hoe jullie dit oplossen :)

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
C++:
1
mijnFiets->SetRotationPosition(blaRot,blaPos); 

;)

Kwestie van een private SetPos en SetRot maken, de public methods SetPosition en SetRotation de mutex laten afhandelen en de interne members SetPos en SetRot laten callen. En dan maak je een extra SetRotationPosition en die laat je beide SetPos en SetRot aanroepen met daaromheen je mutex (un)lock.

[ Voor 71% gewijzigd door RobIII op 28-05-2009 00:00 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
RobIII schreef op woensdag 27 mei 2009 @ 23:50:
C++:
1
mijnFiets->SetRotationPosition(blaRot,blaPos); 

;)

Kwestie van een private SetPos en SetRot maken, de public methods SetPosition en SetRotation de mutex laten afhandelen en de interne members laten callen. En de SetRotationPosition laat je beide SetPos en SetRot aanroepen met daaromheen je mutex (un)lock.
is een mogelijkheid idd, maar vind em niet echt netjes, puristisch gezien.. en stel dat je 10 instelbare variabelen hebt? krijg je een SetPositionPitchHeadingColorDirection(..) enzo :P

misschien zit daar wel iets van een uitweg in.. een functie waarmee je verschillende dingen kunt setten in 1 keer, maar dan wel wat generieker dan deze manier.. :)

Acties:
  • 0 Henk 'm!

  • Dido
  • Registratie: Maart 2002
  • Laatst online: 18:33

Dido

heforshe

* Dido mompelt iets over overloading...

Wat betekent mijn avatar?


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Verwijderd schreef op woensdag 27 mei 2009 @ 23:59:
[...]


is een mogelijkheid idd, maar vind em niet echt netjes, puristisch gezien.. en stel dat je 10 instelbare variabelen hebt? krijg je een SetPositionPitchHeadingColorDirection(..) enzo :P
Nah, dan zou ik al heel gauw een wrapper classe gaan schrijven die dat soort zaken afhandelt. Of een Begin() en Commit() method ofzo welke resp. de mutex locken en unlocken. Maar dat is, zonder een wrapper, net zo "foutgevoelig" als je 1 van beiden vergeten zou.
Dido schreef op donderdag 28 mei 2009 @ 00:00:
* Dido mompelt iets over overloading...
Het is al laat, maar ik zie het even niet? Je bedoelt een mutex meegeven als je 'm gelocked wil hebben en anders niet ofzo?

[ Voor 27% gewijzigd door RobIII op 28-05-2009 00:05 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Sowieso RAII gebruiken voor je locks.

C++:
1
2
3
4
5
6
7
8
9
10
11
void SetPosition(const Vector3 &position) { 
    dataMutex.Lock(); 

    this->position = position; 
// Oops, Vector3 overload de operator= en throwed een exception
// Functie aanroep eindigt hier vanwege de exception

// nooit (meer) aangeroepen. Deadlock.
    dataMutex.Unlock(); 
  } 
};


De simpele dingen doe ik zo:

C++:
1
2
3
4
5
6
7
8
#include <boost/thread.hpp>
class Foo {
  void bar() {
      boost::lock_guard<boost::mutex> lock(m_mutex);
  }

  boost::mutex m_mutex;
}


Als ik locks lang vast wil houden (eigenlijk nooit met locks, maar met conditie variabelen wel regelmatig) zoiets:

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
class Foo {
public:
   class Lock {
      friend class Foo;
      Lock(const Foo& x) : m_foo(x) {m_foo.lock();}
      const Foo& m_foo;
   public:
      ~Lock() {m_foo.unlock();}
   };
   typedef boost::shared_ptr<Lock> LockPtr;

   LockPtr getItem() {return LockPtr(new Lock(*this));}
private:
   void lock(); // do some internal locking/syncing
   void unlock();
};

Foo::LockPtr old_lock;
Foo foo;

while (1)
{ // scope of lock
   Foo::LockPtr lock = foo.getItem();
   // do stuff
   // release an old lock and keep this one
   old_lock = lock; // old lock released
} // lock released at end of scope

[ Voor 61% gewijzigd door Zoijar op 28-05-2009 00:36 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
ook al vanwege het avonduur :P snap ik je laatste voorbeeld niet helemaal.. kun je de werking even toelichten?

exceptions doe ik trouwens niet aan.. dus dat probleem heb ik niet ;) doe dat soort dingen gewoon met return values (ook een discussie op zich waard natuurlijk :P)

zal die lock_guard eens bekijken :)

[ Voor 16% gewijzigd door Verwijderd op 28-05-2009 00:34 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op donderdag 28 mei 2009 @ 00:33:
exceptions doe ik trouwens niet aan..
C++ echter wel. Een operator new die failed, een dynamic_cast op een reference die niet lukt, etc. Daarnast is RAII gewoon een typisch C++ paradigma. Het zorgt voor cleane en robuuste code, en exception safety is daarbij vrijwel automatisch.

Overigens loop ik ook weleens tegen een dergelijk probleem aan. Dan niet met locks, maar wel het feit dat een enkele change een bepaalde overhead heeft, en meerdere losse changes meer overhead met zich meebrengen dan strict noodzakelijk. In zo'n geval definieer ik meestal een object dat de veranderingen bijhoud - een transactie - waar ik die properties op kan zetten alsof ik direct tegen het originele object praat, en als ik klaar ben apply ik dat update object op het origineel.

In jouw context dus:
C++:
1
2
3
4
Spatial::Transaction transaction;
transaction.SetPosition(blaPos);
transaction.SetRotation(blaRot);
mijnFiets->Apply(transaction);


Hiermee kun je tevens zorgen dat changes altijd atomair zijn. De caller is dus niet verantwoordelijk voor rollbacks op het moment dat het ergens tussenin fout gaat.

[ Voor 53% gewijzigd door .oisyn op 28-05-2009 01:19 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Cousin Boneless
  • Registratie: Juni 2008
  • Laatst online: 28-02 12:55
Je zou alle acties kunnen bufferen en dan in één sequence uitvoeren op het object. Probleempje daarbij is wel dat als je iets van het object opvraagt dat afhankelijk is van een eerdere actie, dat je alsnog halverwege moet "flushen", maar als dat niet nodig is, heb je aan het eind een lijstje acties dat binnen één lock uitgevoerd wordt. Sorry voor de bult code:
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    class Actor
    {
        private abstract class AbstractAction 
        {
            public abstract void perform();   
        }

        #region Action1
        private class Action1 : AbstractAction
        {
            public delegate void Action1Callback(int value1, int value2);

            private int value1;
            private int value2;
            private Action1Callback cb;

            public Action1(Action1Callback cb, int value1, int value2)
            {
                this.cb = cb;
                this.value1 = value1;
                this.value2 = value2;
            }

            public override void perform()
            {
                cb(value1, value2);
            }
        }
        #endregion

        #region Action2
        private class Action2 : AbstractAction
        {
            public delegate void Action2Callback(string value1);

            private string value1;
            private Action2Callback cb;

            public Action2(Action2Callback cb, string value1)
            {
                this.cb = cb;
                this.value1 = value1;
            }

            public override void perform()
            {
                cb(value1);
            }
        }
        #endregion

        private LinkedList<AbstractAction> actionList;

        private void performAction1(int value1, int value2)
        {
            Console.WriteLine(string.Format("Action1({0},{1})", value1, value2));
        }

        private void performAction2(string value2)
        {
            Console.WriteLine(string.Format("Action2({0}", value2));
        }

        public void action1(int value1, int value2)
        {
            actionList.AddLast(new Action1(new Action1.Action1Callback(performAction1), value1, value2));
        }

        public void action2(string value1)
        {
            actionList.AddLast(new Action2(new Action2.Action2Callback(performAction2), value1));
        }

        private void setlock()
        {
            // Your code here
        }

        private void releaselock()
        {
            // Your code here
        }

        public void performActions()
        {
            setlock();
            foreach (AbstractAction action in actionList)
            {
                action.perform();
            }
            releaselock();
            actionList.Clear();
        }

        public Actor()
        {
            actionList = new LinkedList<AbstractAction>();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Actor fiets = new Actor();
            fiets.action1(1, 2);
            fiets.action2("Hello world!");
            fiets.performActions();
        }
    }


edit: damn te laat :)

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
tnx oisyn, daar heb ik iets aan :) ziet er clean uit.
edit: en boneless ook natuurlijk :P wat doet iedereen nog wakker.. ;)

dit soort oplossingen via tussenobjecten zijn leuk, doe ik ook steeds meer sinds ik design patterns heb omarmd ;) wel jammer dat het veel code kost.. maarja.. schoonheid uber alles :)

[ Voor 70% gewijzigd door Verwijderd op 28-05-2009 01:23 ]


Acties:
  • 0 Henk 'm!

  • Cousin Boneless
  • Registratie: Juni 2008
  • Laatst online: 28-02 12:55
Ik ben (sluimerend) op zoek naar dit soort patterns voor "very lazy evaluation" in OR mappings..
Lekker z'n gang laten gaan en pas bij het afbeelden bepalen wat je daarvoor werkelijk nodig hebt uit de database.

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Hmm, dit neigt eig'k meer naar SEA dan PRG. *schopt*

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

Verwijderd

Cousin Boneless schreef op donderdag 28 mei 2009 @ 01:48:
Ik ben (sluimerend) op zoek naar dit soort patterns voor "very lazy evaluation" in OR mappings..
Lekker z'n gang laten gaan en pas bij het afbeelden bepalen wat je daarvoor werkelijk nodig hebt uit de database.
Dan moet je misschien eens gaan kijken hoe dat werkt in C# met Linq. Wat dat heeeeel simpel gezegd (lees: weet ik niet 't fijne van) doet is je method calls pas uitvoeren als je daadwerkelijk de resultaten opvraagt.

In Linq to SQL kan je dat goed zien. Wat er gebeurt is dat iedere filter die je op je dataset gooit aan 't eind in een expression tree wordt omgezet, welke bij 'het afbeelden' vertaald wordt naar een SQL query die uiteindelijk uitgevoerd wordt. Ik denk dat er hier wel iemand op 't forum zit (hint: EfBe) die daar heel veel over weet ;-) Maar da's meer een onderwerp voor een apart topic, denk ik.

Acties:
  • 0 Henk 'm!

  • creator1988
  • Registratie: Januari 2007
  • Laatst online: 08:50
Dit komt omdat je expression tree wordt opgeslagen, en niet het resultaat. Je expression tree kan je modificeren, pas bij het itereren (het is een IEnumerable, probeer maar eens iets als
code:
1
2
3
4
5
IEnumerable<int> foo()
{
 for(int i = 0; i < 10; i++)
    yield return i*i;
}
, dan zie je dat de evaluatie pas gebeurd als dit echt nodig is (als je dus foreach(var item in foo()) doet) wordt de expression tree daadwerkelijk uitgevoerd.

Geen idee of C++ ook zoiets kent.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Iterator functions doen niets met expression trees. Feitelijk zijn het gewoon een soort van coroutines (generators). Bovenstaande code wordt gecompileerd naar zoiets:

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
class FooEnumerable : IEnumerable<int>
{
    class FooEnumerator : IEnumerator<int>
    {
        private int m_i = 0;
        private object m_current = null;

        public void Reset()
        {
            throw new NotSupportedException();
        }

        public bool MoveNext()
        {
            if (m_i < 10)
            {
                m_current = m_i * m_i;
                m_i++;
                return true;
            }
            return false;
        }

        public int Current { get { return m_current; } }
    }


    public IEnumerator<int> GetEnumerator() { return new FooEnumerator(); }
}

IEnumerable<int> foo()
{
    return new FooEnumerable();
}


Oftewel, er wordt een IEnumerator geimplementeerd waarin de hele state van de foo() functie gerepresenteert wordt (data die normaal op de stack komt dus), en vervolgens wordt de implementatie van foo() getransformeerd zodat ie reentrant is en in MoveNext() gegoten kan worden.

Logisch ook dat yield niets met expression trees doet, aangezien yield al in C# 2.0 bestond terwijl Expression trees pas in C# 3.0 hun intrede maakte.

[ Voor 117% gewijzigd door .oisyn op 29-05-2009 01:39 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Cousin Boneless
  • Registratie: Juni 2008
  • Laatst online: 28-02 12:55
Ha! Jullie zetten me op het juiste spoor. Ik moet toegeven dat ik bij LINQ altijd het idee had dat dit de zoveelste syntactische sugar van MS was om de Mono verder op achterstand te zetten, maar dit is heel krachtig. Daar word Ik blij van (o.a. omdat ik er alleen nog maar over had gedacht om dit zelf te doen) Moet er wel even goed voor gaan zitten, want die combinatie van OO en functioneel programmeren is best lastig te doorgronden.
http://www.interact-sw.co...005/09/30/expressiontrees
Pagina: 1